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/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 884fe1663a..e624b37579 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,4 +3,5 @@ blank_issues_enabled: false contact_links: - name: Security Contact + url: https://github.com/woile about: Please report security vulnerabilities to santiwilly@gmail.com 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..da3bd5af8a 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 @@ -26,6 +26,6 @@ body: - type: textarea id: related-issue attributes: - label: Additional context + label: Related issues description: | If applicable, add link to existing issue also help us know better. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0064604fba..e37148c0a1 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -9,12 +9,31 @@ 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 +- [ ] Check and fix any broken links (internal or external) in the documentation + +> When running `poetry doc`, any broken internal documentation links will be reported in the console output like this: +> +> ```text +> INFO - Doc file 'config.md' contains a link 'commands/bump.md#-post_bump_hooks', but the doc 'commands/bump.md' does not contain an anchor '#-post_bump_hooks'. +> ``` + +## Expected Behavior @@ -25,5 +44,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/bumpversion.yml b/.github/workflows/bumpversion.yml index ed0c8cffce..d74ed624a7 100644 --- a/.github/workflows/bumpversion.yml +++ b/.github/workflows/bumpversion.yml @@ -12,7 +12,7 @@ jobs: name: "Bump version and create changelog with commitizen" steps: - name: Check out - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}" diff --git a/.github/workflows/docspublish.yml b/.github/workflows/docspublish.yml index a871d3c379..0102856cbc 100644 --- a/.github/workflows/docspublish.yml +++ b/.github/workflows/docspublish.yml @@ -4,17 +4,18 @@ on: push: branches: - master + workflow_dispatch: jobs: update-cli-screenshots: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.x" - name: Install dependencies @@ -42,7 +43,7 @@ jobs: runs-on: ubuntu-latest needs: update-cli-screenshots steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}" fetch-depth: 0 @@ -50,7 +51,7 @@ jobs: run: | git pull origin master - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.x" - name: Install dependencies @@ -58,21 +59,19 @@ jobs: python -m pip install -U pip poetry poethepoet poetry --version poetry install --no-root --only documentation - - name: Build docs - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - poetry doc:build - name: Generate Sponsors 💖 uses: JamesIves/github-sponsors-readme-action@v1 with: token: ${{ secrets.PERSONAL_ACCESS_TOKEN_FOR_ORG }} file: "docs/README.md" - - name: Push doc to Github Page - uses: peaceiris/actions-gh-pages@v4 + organization: true + - name: Build docs + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + poetry doc:build + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@v4 with: - personal_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - publish_branch: gh-pages - publish_dir: ./site - user_name: "github-actions[bot]" - user_email: "github-actions[bot]@users.noreply.github.com" + folder: ./site # The folder the action should deploy. + branch: gh-pages diff --git a/.github/workflows/homebrewpublish.yml b/.github/workflows/homebrewpublish.yml index 443a13b1ba..3ca67da811 100644 --- a/.github/workflows/homebrewpublish.yml +++ b/.github/workflows/homebrewpublish.yml @@ -12,9 +12,9 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.x" - name: Install dependencies @@ -24,7 +24,7 @@ jobs: run: | echo "project_version=$(cz version --project)" >> $GITHUB_ENV - name: Update Homebrew formula - uses: dawidd6/action-homebrew-bump-formula@v4 + uses: dawidd6/action-homebrew-bump-formula@v7 with: token: ${{secrets.PERSONAL_ACCESS_TOKEN}} formula: commitizen diff --git a/.github/workflows/label_issues.yml b/.github/workflows/label_issues.yml index 45ca450f89..359c50d65a 100644 --- a/.github/workflows/label_issues.yml +++ b/.github/workflows/label_issues.yml @@ -12,12 +12,35 @@ jobs: issues: write runs-on: ubuntu-latest steps: - - uses: actions/github-script@v7 + - uses: actions/github-script@v8 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/.github/workflows/label_pr.yml b/.github/workflows/label_pr.yml index b409c8b757..8e2d674f2b 100644 --- a/.github/workflows/label_pr.yml +++ b/.github/workflows/label_pr.yml @@ -9,11 +9,39 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: sparse-checkout: | .github/labeler.yml sparse-checkout-cone-mode: false - - uses: actions/labeler@v5 + - uses: actions/labeler@v6 with: configuration-path: .github/labeler.yml + - name: Label based on PR title + uses: actions/github-script@v8 + with: + script: | + const title = context.payload.pull_request.title.toLowerCase(); + const labels = []; + + if (title.includes("fix") || title.includes("bug")) { + labels.push("type: bug"); + } + if (title.includes("feat")) { + labels.push("type: feature"); + } + if (title.includes("doc")) { + labels.push("type: documentation"); + } + if (title.includes("refactor")) { + labels.push("type: refactor"); + } + + if (labels.length > 0) { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + labels + }); + } diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index b50b02a681..4e4738ac90 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -6,15 +6,15 @@ jobs: python-check: strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] platform: [ubuntu-22.04, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index dc522bc0fd..de078815fa 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -9,12 +9,12 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}" fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.x" - name: Install dependencies diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 08c31ba0a0..f70fbf2f10 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: - id: debug-statements - id: no-commit-to-branch - id: check-merge-conflict - - id: check-toml + - id: check-toml # TOML linter (syntax checker) - id: check-yaml args: [ '--unsafe' ] # for mkdocs.yml - id: detect-private-key @@ -48,24 +48,29 @@ repos: - tomli - repo: https://github.com/commitizen-tools/commitizen - rev: v4.6.3 # automatically updated by Commitizen + rev: v4.10.1 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch stages: - post-commit + - repo: https://github.com/ComPWA/taplo-pre-commit + rev: v0.9.3 + hooks: + - id: taplo-format + - repo: local hooks: - id: format - name: Format + name: Format Python code via Poetry language: system pass_filenames: false entry: poetry format types: [ python ] - id: linter and test - name: Linters + name: Linters via Poetry language: system pass_filenames: false entry: poetry lint diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 2a3a088484..c142634581 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -24,4 +24,4 @@ pass_filenames: false language: python language_version: python3 - minimum_pre_commit_version: "1.4.3" + minimum_pre_commit_version: "3.2.0" diff --git a/.taplo.toml b/.taplo.toml new file mode 100644 index 0000000000..36a3453ac9 --- /dev/null +++ b/.taplo.toml @@ -0,0 +1,4 @@ +include = ["pyproject.toml", ".taplo.toml"] + +[formatting] +indent_string = " " diff --git a/CHANGELOG.md b/CHANGELOG.md index dbad463955..7ee748085f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,215 @@ +## v4.10.1 (2025-12-11) + +### Fix + +- **version**: fix the behavior of cz version --major +- **cli**: debug and no_raise can be used together in sys.excepthook +- **git**: replace lstrip with strip for compatibility issue +- **bump**: remove NotAllowed related to --get-next option, other related refactoring + +### Refactor + +- **version**: rename class member to align with other classes +- **cargo_provider**: cleanup and get rid of potential type errors +- **bump**: extract option validation and new version resolution to new functions +- **changelog**: raise NotAllow when file_name not passed instead of using assert +- **bump**: rename parameter and variables + +### Perf + +- **ruff**: enable ruff rules TC001~TC006 +- add TYPE_CHECKING to CzQuestion imports + +## v4.10.0 (2025-11-10) + +### Feat + +- add config option for line length warning +- **conventional_commits**: allow exclamation in title on BC +- **version**: add the ability to just print major or minor version +- allow `amend!` prefix as created by `git --fixup=reword:` + +### Fix + +- **commands/version**: add missing return +- **test**: set terminal width for cli tests +- **Init**: raise InitFailedError on keyboard interrupt on pre-commit hook question, simplify logic, remove unreachable code path + +### Refactor + +- **bump**: cleanup related to update_version_file +- **RestructuredTest**: rename variable, fix typo and remove unnecessary string copy +- **TomlConfig**: minor cleanups for DX +- **Commit**: refactor _prompt_commit_questions and fix some type hint +- **hooks**: refactor to improve readability +- **Init**: make project_info a module and remove self.project_info +- **BaseConfig**: update docstring, extract factory method and remove unnecessary variable assignment +- remove self.encoding for better maintainability +- **utils**: make get_backup_file_path to return a path for semantic correctness +- remove unnecessary class member tag_format +- **Bump**: remove use of getattr +- **ConventionalCommitsCz**: rewrite message method to make the pattern more clear +- **cmd**: unnest try except +- **BaseCommitizen**: remove NotImplementedError and make them abstract method +- **BaseCommitizen**: construct Style object directly to get rid of potential type error + +## v4.9.1 (2025-09-10) + +### Fix + +- **dependency**: move deprecated to project.dependencies + +## v4.9.0 (2025-09-09) + +### Feat + +- **check**: add check against default branch + +### Fix + +- **changelog**: mark get_smart_tag_range as deprecated +- **init**: use pre-push as pre-commit stage +- **init**: use pre-push as pre-commit stage +- **init**: make welcome message easier to read +- **Init**: fix a typo in _ask_version_provider options and remove unnecessary filter, use named tuple for options +- **ExitCode**: add from_str in ExitCode and replace parse_no_raise with it +- raise NoVersionSpecifiedError if version is None, and adjust call sites of get_version +- **Changelog**: fix _export_template variable type +- **Bump**: rewrite --get-next NotAllowed error message for consistency + +### Refactor + +- **changelog**: add get_next_tag_name_after_version and test, mark unused for get_smart_tag_range +- **changelog**: simplify logic for get_oldest_and_newest_rev +- **changelog**: shorten generate_tree_from_commits +- **Init**: remove the variable values_to_add and the update_config function for readability +- **Init**: remove unnecessary methods from ProjectInfo and refactor _ask_tag +- **Init**: fix unbounded variable in _ask_tag_format +- **init**: remote extra words +- **process_commit_message**: better type and early return +- **Init**: extract _get_config_data for readability +- **changelog**: shorten condition expression and early return +- **Changelog**: remove unnecessary intermediate variables for better readability +- **bump**: use a loop to shorten a series of similar NotAllowed exceptions +- **Init**: use ternary operator +- **TagRules**: extract tag_formats property and simplify list comprehension +- **git**: remove redundant if branch +- **ScmProvider**: replace sorted with max +- **ExpectedExit**: make the constructor more compact +- **ParseArgs**: simplify __call__ function body + +## v4.8.4 (2025-09-05) + +### Fix + +- members in workspace, use exclude +- cargo workspaces + +### Refactor + +- reduce code indentation + +## v4.8.3 (2025-06-09) + +### Fix + +- **cli**: update description for deprecate warning +- **commit**: emit deprecated warning of cz commit -s +- **Check**: make parameters backward compatible +- **BaseConfig**: mypy error +- **deprecated**: mark deprecate in v5 +- **defaults**: add non-capitalized default constants back and deprecated warning + +### Refactor + +- **jira**: refactor message +- **conventional_commits**: use TypedDict for answers +- **conventional_commits**: make schema_pattern more readable +- do not guess if changelog format is provided +- **check**: compile once and rename variable +- **questions**: type questions with TypedDict +- **bump**: simplify nested if +- **git**: retype get_commits parameter to make it more friendly to call sites +- **git**: simplify tag logic +- **bump**: eliminate similar patterns in code +- **bump**: use any to replace 'or' chain +- remove unnecessary bool() and remove Any type from TypedDict get +- **bump**: improve readability and still bypass mypy check +- **commands**: remove unused args, type version command args +- **commit**: type commit args +- **check**: type CheckArgs arguments +- **check**: remove unused argument +- **changelog**: type untyped arguments +- **bump**: TypedDict for bump argument +- make methods protected, better type +- **conventional_commits**: remove unnecessary checks +- fix mypy output and better type +- **BaseCommitizen**: remove unused process_commit +- remove `TypeError` handling since `Python >=3.9` is required +- add comment clarifying `no_raise` parsing to `list[int]` +- **cli.py**: add type hints +- **mypy**: remove `unused-ignore` +- **changelog**: better typing, yield +- **cli**: early return and improve test coverage +- **git**: extract _create_commit_cmd_string +- misc cleanup +- **bump**: clean up +- **bump**: add type for out, replace function with re escape +- **BaseConfig**: use setter +- **changelog**: minor cleanup +- **git**: refactor get_tag_names +- **EOLType**: add eol enum back and reorganize methods +- **git**: code cleanup and better test coverage +- **commit**: simplify call +- **version_scheme**: cleanup +- improve readability and fix typos + +### Perf + +- **bump**: avoid unnecessary list construction and rename variable to avoid confusion +- **tags**: use set + +## 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 + +- **providers**: add support for `Cargo.lock` + +### Refactor + +- **tests**: increase verbosity of variables + ## v4.6.3 (2025-05-07) ### Fix diff --git a/commitizen/__init__.py b/commitizen/__init__.py index f16def4441..6db9e6e7db 100644 --- a/commitizen/__init__.py +++ b/commitizen/__init__.py @@ -1,7 +1,7 @@ import logging import logging.config -from colorama import init # type: ignore +from colorama import init from commitizen.cz.base import BaseCommitizen diff --git a/commitizen/__version__.py b/commitizen/__version__.py index de3842dc77..4e499ea83a 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.6.3" +__version__ = "4.10.1" diff --git a/commitizen/bump.py b/commitizen/bump.py index adfab64cb0..cb572d3612 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -6,12 +6,16 @@ from glob import iglob from logging import getLogger from string import Template -from typing import cast +from typing import TYPE_CHECKING, cast -from commitizen.defaults import MAJOR, MINOR, PATCH, bump_message, encoding +from commitizen.defaults import BUMP_MESSAGE, MAJOR, MINOR, PATCH from commitizen.exceptions import CurrentVersionNotFoundError from commitizen.git import GitCommit, smart_open -from commitizen.version_schemes import Increment, Version + +if TYPE_CHECKING: + from collections.abc import Generator, Iterable + + from commitizen.version_schemes import Increment, Version VERSION_TYPES = [None, PATCH, MINOR, MAJOR] @@ -55,16 +59,16 @@ def find_increment( if increment == MAJOR: break - return cast(Increment, increment) + return cast("Increment", increment) def update_version_in_files( current_version: str, new_version: str, - files: list[str], + version_files: Iterable[str], *, - check_consistency: bool = False, - encoding: str = encoding, + check_consistency: bool, + encoding: str, ) -> list[str]: """Change old version to the new one in every file given. @@ -74,16 +78,22 @@ def update_version_in_files( Returns the list of updated files. """ - # TODO: separate check step and write step - updated = [] - for path, regex in files_and_regexs(files, current_version): - current_version_found, version_file = _bump_with_regex( - path, - current_version, - new_version, - regex, - encoding=encoding, - ) + updated_files = [] + + for path, pattern in _resolve_files_and_regexes(version_files, current_version): + current_version_found = False + bumped_lines = [] + + with open(path, encoding=encoding) as version_file: + for line in version_file: + bumped_line = ( + line.replace(current_version, new_version) + if pattern.search(line) + else line + ) + + current_version_found = current_version_found or bumped_line != line + bumped_lines.append(bumped_line) if check_consistency and not current_version_found: raise CurrentVersionNotFoundError( @@ -92,54 +102,32 @@ def update_version_in_files( "version_files are possibly inconsistent." ) + bumped_version_file_content = "".join(bumped_lines) + # Write the file out again with smart_open(path, "w", encoding=encoding) as file: - file.write(version_file) - updated.append(path) - return updated + file.write(bumped_version_file_content) + updated_files.append(path) + + return updated_files -def files_and_regexs(patterns: list[str], version: str) -> list[tuple[str, str]]: +def _resolve_files_and_regexes( + patterns: Iterable[str], version: str +) -> Generator[tuple[str, re.Pattern], None, None]: """ Resolve all distinct files with their regexp from a list of glob patterns with optional regexp """ - out = [] + filepath_set: set[tuple[str, str]] = set() for pattern in patterns: drive, tail = os.path.splitdrive(pattern) path, _, regex = tail.partition(":") filepath = drive + path - if not regex: - regex = _version_to_regex(version) - - for path in iglob(filepath): - out.append((path, regex)) - return sorted(list(set(out))) + regex = regex or re.escape(version) + filepath_set.update((path, regex) for path in iglob(filepath)) -def _bump_with_regex( - version_filepath: str, - current_version: str, - new_version: str, - regex: str, - encoding: str = encoding, -) -> tuple[bool, str]: - current_version_found = False - lines = [] - pattern = re.compile(regex) - with open(version_filepath, encoding=encoding) as f: - for line in f: - if pattern.search(line): - bumped_line = line.replace(current_version, new_version) - if bumped_line != line: - current_version_found = True - lines.append(bumped_line) - else: - lines.append(line) - return current_version_found, "".join(lines) - - -def _version_to_regex(version: str) -> str: - return version.replace(".", r"\.").replace("+", r"\+") + return ((path, re.compile(regex)) for path, regex in sorted(filepath_set)) def create_commit_message( @@ -148,6 +136,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/changelog.py b/commitizen/changelog.py index 704efe6071..dfc4157725 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -29,11 +29,12 @@ import re from collections import OrderedDict, defaultdict -from collections.abc import Iterable from dataclasses import dataclass from datetime import date -from typing import TYPE_CHECKING +from itertools import chain +from typing import TYPE_CHECKING, Any +from deprecated import deprecated from jinja2 import ( BaseLoader, ChoiceLoader, @@ -42,13 +43,14 @@ Template, ) -from commitizen.cz.base import ChangelogReleaseHook from commitizen.exceptions import InvalidConfigurationError, NoCommitsFoundError -from commitizen.git import GitCommit, GitTag from commitizen.tags import TagRules if TYPE_CHECKING: - from commitizen.cz.base import MessageBuilderHook + from collections.abc import Generator, Iterable, Mapping, MutableMapping, Sequence + + from commitizen.cz.base import ChangelogReleaseHook, MessageBuilderHook + from commitizen.git import GitCommit, GitTag @dataclass @@ -63,7 +65,7 @@ class Metadata: latest_version_position: int | None = None latest_version_tag: str | None = None - def __post_init__(self): + def __post_init__(self) -> None: if self.latest_version and not self.latest_version_tag: # Test syntactic sugar # latest version tag is optional if same as latest version @@ -84,37 +86,36 @@ def generate_tree_from_commits( changelog_message_builder_hook: MessageBuilderHook | None = None, changelog_release_hook: ChangelogReleaseHook | None = None, rules: TagRules | None = None, -) -> Iterable[dict]: +) -> Generator[dict[str, Any], None, None]: pat = re.compile(changelog_pattern) map_pat = re.compile(commit_parser, re.MULTILINE) body_map_pat = re.compile(commit_parser, re.MULTILINE | re.DOTALL) - current_tag: GitTag | None = None rules = rules or TagRules() # Check if the latest commit is not tagged - if commits: - latest_commit = commits[0] - current_tag = get_commit_tag(latest_commit, tags) - - current_tag_name: str = unreleased_version or "Unreleased" - current_tag_date: str = "" - if unreleased_version is not None: - current_tag_date = date.today().isoformat() - if current_tag is not None and current_tag.name: - current_tag_name = current_tag.name - current_tag_date = current_tag.date + current_tag = get_commit_tag(commits[0], tags) if commits else None + current_tag_name = unreleased_version or "Unreleased" + current_tag_date = ( + date.today().isoformat() if unreleased_version is not None else "" + ) + + used_tags: set[GitTag] = set() + if current_tag: + used_tags.add(current_tag) + if current_tag.name: + current_tag_name = current_tag.name + current_tag_date = current_tag.date + + commit_tag: GitTag | None = None changes: dict = defaultdict(list) - used_tags: list = [current_tag] for commit in commits: - commit_tag = get_commit_tag(commit, tags) - if ( - commit_tag + (commit_tag := get_commit_tag(commit, tags)) and commit_tag not in used_tags and rules.include_in_changelog(commit_tag) ): - used_tags.append(commit_tag) + used_tags.add(commit_tag) release = { "version": current_tag_name, "date": current_tag_date, @@ -127,24 +128,15 @@ def generate_tree_from_commits( current_tag_date = commit_tag.date changes = defaultdict(list) - matches = pat.match(commit.message) - if not matches: + if not pat.match(commit.message): continue - # Process subject from commit message - if message := map_pat.match(commit.message): - process_commit_message( - changelog_message_builder_hook, - message, - commit, - changes, - change_type_map, - ) - - # Process body from commit message - body_parts = commit.body.split("\n\n") - for body_part in body_parts: - if message := body_map_pat.match(body_part): + # Process subject and body from commit message + for message in chain( + [map_pat.match(commit.message)], + (body_map_pat.match(block) for block in commit.body.split("\n\n")), + ): + if message: process_commit_message( changelog_message_builder_hook, message, @@ -167,10 +159,10 @@ def process_commit_message( hook: MessageBuilderHook | None, parsed: re.Match[str], commit: GitCommit, - changes: dict[str | None, list], - change_type_map: dict[str, str] | None = None, -): - message: dict = { + ref_changes: MutableMapping[str | None, list], + change_type_map: Mapping[str, str] | None = None, +) -> None: + message: dict[str, Any] = { "sha1": commit.rev, "parents": commit.parents, "author": commit.author, @@ -178,33 +170,39 @@ def process_commit_message( **parsed.groupdict(), } - if processed := hook(message, commit) if hook else message: - messages = [processed] if isinstance(processed, dict) else processed - for msg in messages: - change_type = msg.pop("change_type", None) - if change_type_map: - change_type = change_type_map.get(change_type, change_type) - changes[change_type].append(msg) + processed_msg = hook(message, commit) if hook else message + if not processed_msg: + return + messages = [processed_msg] if isinstance(processed_msg, dict) else processed_msg + for msg in messages: + change_type = msg.pop("change_type", None) + if change_type_map: + change_type = change_type_map.get(change_type, change_type) + ref_changes[change_type].append(msg) -def order_changelog_tree(tree: Iterable, change_type_order: list[str]) -> Iterable: + +def generate_ordered_changelog_tree( + tree: Iterable[Mapping[str, Any]], change_type_order: list[str] +) -> Generator[dict[str, Any], None, None]: if len(set(change_type_order)) != len(change_type_order): raise InvalidConfigurationError( - f"Change types contain duplicates types ({change_type_order})" + f"Change types contain duplicated types ({change_type_order})" ) - sorted_tree = [] for entry in tree: - ordered_change_types = change_type_order + sorted( - set(entry["changes"].keys()) - set(change_type_order) - ) - changes = [ - (ct, entry["changes"][ct]) - for ct in ordered_change_types - if ct in entry["changes"] - ] - sorted_tree.append({**entry, **{"changes": OrderedDict(changes)}}) - return sorted_tree + yield { + **entry, + "changes": _calculate_sorted_changes(change_type_order, entry["changes"]), + } + + +def _calculate_sorted_changes( + change_type_order: list[str], changes: Mapping[str, Any] +) -> OrderedDict[str, Any]: + remaining_change_types = set(changes.keys()) - set(change_type_order) + sorted_change_types = change_type_order + sorted(remaining_change_types) + return OrderedDict((ct, changes[ct]) for ct in sorted_change_types if ct in changes) def get_changelog_template(loader: BaseLoader, template: str) -> Template: @@ -222,7 +220,7 @@ def render_changelog( tree: Iterable, loader: BaseLoader, template: str, - **kwargs, + **kwargs: Any, ) -> str: jinja_template = get_changelog_template(loader, template) changelog: str = jinja_template.render(tree=tree, **kwargs) @@ -248,6 +246,7 @@ def incremental_build( unreleased_start = metadata.unreleased_start unreleased_end = metadata.unreleased_end latest_version_position = metadata.latest_version_position + skip = False output_lines: list[str] = [] for index, line in enumerate(lines): @@ -257,9 +256,7 @@ def incremental_build( skip = False if ( latest_version_position is None - or isinstance(latest_version_position, int) - and isinstance(unreleased_end, int) - and latest_version_position > unreleased_end + or latest_version_position > unreleased_end ): continue @@ -268,18 +265,34 @@ def incremental_build( if index == latest_version_position: output_lines.extend([new_content, "\n"]) - output_lines.append(line) - if not isinstance(latest_version_position, int): - if output_lines and output_lines[-1].strip(): - # Ensure at least one blank line between existing and new content. - output_lines.append("\n") - output_lines.append(new_content) + + if latest_version_position is not None: + return output_lines + + if output_lines and output_lines[-1].strip(): + # Ensure at least one blank line between existing and new content. + output_lines.append("\n") + output_lines.append(new_content) return output_lines +def get_next_tag_name_after_version(tags: Iterable[GitTag], version: str) -> str | None: + it = iter(tag.name for tag in tags) + for name in it: + if name == version: + return next(it, None) + + raise NoCommitsFoundError(f"Could not find a valid revision range. {version=}") + + +@deprecated( + reason="This function is unused and will be removed in v5", + version="5.0.0", + category=DeprecationWarning, +) def get_smart_tag_range( - tags: list[GitTag], newest: str, oldest: str | None = None + tags: Sequence[GitTag], newest: str, oldest: str | None = None ) -> list[GitTag]: """Smart because it finds the N+1 tag. @@ -305,49 +318,36 @@ def get_smart_tag_range( def get_oldest_and_newest_rev( - tags: list[GitTag], + tags: Iterable[GitTag], version: str, rules: TagRules, -) -> tuple[str | None, str | None]: +) -> tuple[str | None, str]: """Find the tags for the given version. `version` may come in different formats: - `0.1.0..0.4.0`: as a range - `0.3.0`: as a single version """ - oldest: str | None = None - newest: str | None = None - try: - oldest, newest = version.split("..") - except ValueError: - newest = version - if not (newest_tag := rules.find_tag_for(tags, newest)): + oldest_version, sep, newest_version = version.partition("..") + if not sep: + newest_version = version + oldest_version = "" + + def get_tag_name(v: str) -> str: + if tag := rules.find_tag_for(tags, v): + return tag.name raise NoCommitsFoundError("Could not find a valid revision range.") - oldest_tag = None - oldest_tag_name = None - if oldest: - if not (oldest_tag := rules.find_tag_for(tags, oldest)): - raise NoCommitsFoundError("Could not find a valid revision range.") - oldest_tag_name = oldest_tag.name + newest_tag_name = get_tag_name(newest_version) + oldest_tag_name = get_tag_name(oldest_version) if oldest_version else None - tags_range = get_smart_tag_range( - tags, newest=newest_tag.name, oldest=oldest_tag_name + oldest_rev = get_next_tag_name_after_version( + tags, oldest_tag_name or newest_tag_name ) - if not tags_range: - raise NoCommitsFoundError("Could not find a valid revision range.") - - oldest_rev: str | None = tags_range[-1].name - newest_rev = newest_tag.name - - # check if it's the first tag created - # and it's also being requested as part of the range - if oldest_rev == tags[-1].name and oldest_rev == oldest_tag_name: - return None, newest_rev - - # when they are the same, and it's also the - # first tag created - if oldest_rev == newest_rev: - return None, newest_rev - return oldest_rev, newest_rev + # Return None for oldest_rev if: + # 1. The oldest tag is the last tag in the list and matches the requested oldest tag + # 2. The oldest and the newest tag are the same + if oldest_rev == newest_tag_name: + return None, newest_tag_name + return oldest_rev, newest_tag_name diff --git a/commitizen/changelog_formats/__init__.py b/commitizen/changelog_formats/__init__.py index 782bfb24cb..e4eb5e0a59 100644 --- a/commitizen/changelog_formats/__init__.py +++ b/commitizen/changelog_formats/__init__.py @@ -1,17 +1,19 @@ from __future__ import annotations import sys -from typing import ClassVar, Protocol +from typing import TYPE_CHECKING, Callable, ClassVar, Protocol if sys.version_info >= (3, 10): from importlib import metadata else: import importlib_metadata as metadata -from commitizen.changelog import Metadata -from commitizen.config.base_config import BaseConfig from commitizen.exceptions import ChangelogFormatUnknown +if TYPE_CHECKING: + from commitizen.changelog import Metadata + from commitizen.config.base_config import BaseConfig + CHANGELOG_FORMAT_ENTRYPOINT = "commitizen.changelog_format" TEMPLATE_EXTENSION = "j2" @@ -25,7 +27,7 @@ class ChangelogFormat(Protocol): config: BaseConfig - def __init__(self, config: BaseConfig): + def __init__(self, config: BaseConfig) -> None: self.config = config @property @@ -64,10 +66,9 @@ def get_changelog_format( :raises FormatUnknown: if a non-empty name is provided but cannot be found in the known formats """ name: str | None = config.settings.get("changelog_format") - format: type[ChangelogFormat] | None = guess_changelog_format(filename) - - if name and name in KNOWN_CHANGELOG_FORMATS: - format = KNOWN_CHANGELOG_FORMATS[name] + format = ( + name and KNOWN_CHANGELOG_FORMATS.get(name) or _guess_changelog_format(filename) + ) if not format: raise ChangelogFormatUnknown(f"Unknown changelog format '{name}'") @@ -75,7 +76,7 @@ def get_changelog_format( return format(config) -def guess_changelog_format(filename: str | None) -> type[ChangelogFormat] | None: +def _guess_changelog_format(filename: str | None) -> type[ChangelogFormat] | None: """ Try guessing the file format from the filename. @@ -91,3 +92,9 @@ def guess_changelog_format(filename: str | None) -> type[ChangelogFormat] | None if filename.endswith(f".{alt_extension}"): return format return None + + +def __getattr__(name: str) -> Callable[[str], type[ChangelogFormat] | None]: + if name == "guess_changelog_format": + return _guess_changelog_format + raise AttributeError(f"module {__name__} has no attribute {name}") diff --git a/commitizen/changelog_formats/base.py b/commitizen/changelog_formats/base.py index f69cf8f00f..eef6a23689 100644 --- a/commitizen/changelog_formats/base.py +++ b/commitizen/changelog_formats/base.py @@ -2,15 +2,17 @@ import os from abc import ABCMeta -from typing import IO, Any, ClassVar +from typing import IO, TYPE_CHECKING, Any, ClassVar from commitizen.changelog import Metadata -from commitizen.config.base_config import BaseConfig from commitizen.tags import TagRules, VersionTag from commitizen.version_schemes import get_version_scheme from . import ChangelogFormat +if TYPE_CHECKING: + from commitizen.config.base_config import BaseConfig + class BaseFormat(ChangelogFormat, metaclass=ABCMeta): """ @@ -20,15 +22,13 @@ class BaseFormat(ChangelogFormat, metaclass=ABCMeta): extension: ClassVar[str] = "" alternative_extensions: ClassVar[set[str]] = set() - def __init__(self, config: BaseConfig): + def __init__(self, config: BaseConfig) -> None: # Constructor needs to be redefined because `Protocol` prevent instantiation by default # See: https://bugs.python.org/issue44807 self.config = config - self.encoding = self.config.settings["encoding"] - self.tag_format = self.config.settings["tag_format"] self.tag_rules = TagRules( scheme=get_version_scheme(self.config.settings), - tag_format=self.tag_format, + tag_format=self.config.settings["tag_format"], legacy_tag_formats=self.config.settings["legacy_tag_formats"], ignored_tag_formats=self.config.settings["ignored_tag_formats"], ) @@ -37,7 +37,9 @@ def get_metadata(self, filepath: str) -> Metadata: if not os.path.isfile(filepath): return Metadata() - with open(filepath, encoding=self.encoding) as changelog_file: + with open( + filepath, encoding=self.config.settings["encoding"] + ) as changelog_file: return self.get_metadata_from_file(changelog_file) def get_metadata_from_file(self, file: IO[Any]) -> Metadata: diff --git a/commitizen/changelog_formats/restructuredtext.py b/commitizen/changelog_formats/restructuredtext.py index b7e4e105a1..e8ce411e80 100644 --- a/commitizen/changelog_formats/restructuredtext.py +++ b/commitizen/changelog_formats/restructuredtext.py @@ -1,92 +1,88 @@ from __future__ import annotations -import sys from itertools import zip_longest -from typing import IO, TYPE_CHECKING, Any, Union +from typing import IO from commitizen.changelog import Metadata from .base import BaseFormat -if TYPE_CHECKING: - # TypeAlias is Python 3.10+ but backported in typing-extensions - if sys.version_info >= (3, 10): - from typing import TypeAlias - else: - from typing_extensions import TypeAlias - - -# Can't use `|` operator and native type because of https://bugs.python.org/issue42233 only fixed in 3.10 -TitleKind: TypeAlias = Union[str, tuple[str, str]] - class RestructuredText(BaseFormat): extension = "rst" - def get_metadata_from_file(self, file: IO[Any]) -> Metadata: + def get_metadata_from_file(self, file: IO[str]) -> Metadata: """ RestructuredText section titles are not one-line-based, they spread on 2 or 3 lines and levels are not predefined - but determined byt their occurrence order. + but determined by their occurrence order. It requires its own algorithm. For a more generic approach, you need to rely on `docutils`. """ - meta = Metadata() - unreleased_title_kind: TitleKind | None = None - in_overlined_title = False - lines = file.readlines() + out_metadata = Metadata() + unreleased_title_kind: str | tuple[str, str] | None = None + is_overlined_title = False + lines = [line.strip().lower() for line in file.readlines()] + for index, (first, second, third) in enumerate( zip_longest(lines, lines[1:], lines[2:], fillvalue="") ): - first = first.strip().lower() - second = second.strip().lower() - third = third.strip().lower() title: str | None = None - kind: TitleKind | None = None - if self.is_overlined_title(first, second, third): + kind: str | tuple[str, str] | None = None + if _is_overlined_title(first, second, third): title = second kind = (first[0], third[0]) - in_overlined_title = True - elif not in_overlined_title and self.is_underlined_title(first, second): + is_overlined_title = True + elif not is_overlined_title and _is_underlined_title(first, second): title = first kind = second[0] else: - in_overlined_title = False - - if title: - if "unreleased" in title: - unreleased_title_kind = kind - meta.unreleased_start = index - continue - elif unreleased_title_kind and unreleased_title_kind == kind: - meta.unreleased_end = index - # Try to find the latest release done - if version := self.tag_rules.search_version(title): - meta.latest_version = version[0] - meta.latest_version_tag = version[1] - meta.latest_version_position = index - break - if meta.unreleased_start is not None and meta.unreleased_end is None: - meta.unreleased_end = ( - meta.latest_version_position if meta.latest_version else index + 1 + is_overlined_title = False + + if not title: + continue + + if "unreleased" in title: + unreleased_title_kind = kind + out_metadata.unreleased_start = index + continue + + if unreleased_title_kind and unreleased_title_kind == kind: + out_metadata.unreleased_end = index + # Try to find the latest release done + if version := self.tag_rules.search_version(title): + out_metadata.latest_version = version[0] + out_metadata.latest_version_tag = version[1] + out_metadata.latest_version_position = index + break + + if ( + out_metadata.unreleased_start is not None + and out_metadata.unreleased_end is None + ): + out_metadata.unreleased_end = ( + out_metadata.latest_version_position + if out_metadata.latest_version + else len(lines) ) - return meta - - def is_overlined_title(self, first: str, second: str, third: str) -> bool: - return ( - len(first) >= len(second) - and len(first) == len(third) - and all(char == first[0] for char in first[1:]) - and first[0] == third[0] - and self.is_underlined_title(second, third) - ) - - def is_underlined_title(self, first: str, second: str) -> bool: - return ( - len(second) >= len(first) - and not second.isalnum() - and all(char == second[0] for char in second[1:]) - ) + return out_metadata + + +def _is_overlined_title(first: str, second: str, third: str) -> bool: + return ( + len(first) == len(third) >= len(second) + and first[0] == third[0] + and all(char == first[0] for char in first) + and _is_underlined_title(second, third) + ) + + +def _is_underlined_title(first: str, second: str) -> bool: + return ( + len(second) >= len(first) + and not second.isalnum() + and all(char == second[0] for char in second) + ) diff --git a/commitizen/cli.py b/commitizen/cli.py index 72d824380d..e5538aeb49 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -3,12 +3,11 @@ import argparse import logging import sys -from collections.abc import Sequence from copy import deepcopy from functools import partial from pathlib import Path from types import TracebackType -from typing import Any +from typing import TYPE_CHECKING, cast import argcomplete from decli import cli @@ -48,21 +47,18 @@ def __call__( self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, - kwarg: str | Sequence[Any] | None, + values: object, option_string: str | None = None, - ): - if not isinstance(kwarg, str): + ) -> None: + if not isinstance(values, str): return - if "=" not in kwarg: + + key, sep, value = values.partition("=") + if not key or not sep: raise InvalidCommandArgumentError( f"Option {option_string} expect a key=value format" ) kwargs = getattr(namespace, self.dest, None) or {} - key, value = kwarg.split("=", 1) - if not key: - raise InvalidCommandArgumentError( - f"Option {option_string} expect a key=value format" - ) kwargs[key] = value.strip("'\"") setattr(namespace, self.dest, kwargs) @@ -86,9 +82,8 @@ def __call__( data = { "prog": "cz", "description": ( - "Commitizen is a cli tool to generate conventional commits.\n" - "For more information about the topic go to " - "https://conventionalcommits.org/" + "Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management.\n" + "For more information, please visit https://commitizen-tools.github.io/commitizen" ), "formatter_class": argparse.RawDescriptionHelpFormatter, "arguments": [ @@ -105,7 +100,7 @@ def __call__( "name": ["-nr", "--no-raise"], "type": str, "required": False, - "help": "comma separated error codes that won't rise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-tools.github.io/commitizen/exit_codes/", + "help": "comma separated error codes that won't raise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-tools.github.io/commitizen/exit_codes/", }, ], "subcommands": { @@ -149,7 +144,7 @@ def __call__( { "name": ["-s", "--signoff"], "action": "store_true", - "help": "sign off the commit", + "help": "Deprecated, use 'cz commit -- -s' instead", }, { "name": ["-a", "--all"], @@ -165,7 +160,6 @@ def __call__( { "name": ["-l", "--message-length-limit"], "type": int, - "default": 0, "help": "length limit of the commit message; 0 for no limit", }, { @@ -349,7 +343,7 @@ def __call__( }, { "name": ["--version-type"], - "help": "Deprecated, use --version-scheme", + "help": "Deprecated, use --version-scheme instead", "default": None, "choices": version_schemes.KNOWN_SCHEMES, }, @@ -450,6 +444,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", + }, ], }, { @@ -472,6 +470,13 @@ def __call__( "help": "a range of git rev to check. e.g, master..HEAD", "exclusive_group": "group1", }, + { + "name": ["-d", "--use-default-range"], + "action": "store_true", + "default": False, + "help": "check from the default branch to HEAD. e.g, refs/remotes/origin/master..HEAD", + "exclusive_group": "group1", + }, { "name": ["-m", "--message"], "help": "commit message that needs to be checked", @@ -493,7 +498,6 @@ def __call__( { "name": ["-l", "--message-length-limit"], "type": int, - "default": 0, "help": "length limit of the commit message; 0 for no limit", }, ], @@ -537,37 +541,45 @@ def __call__( "action": "store_true", "exclusive_group": "group1", }, + { + "name": ["--major"], + "help": "get just the major version. Need to be used with --project or --verbose.", + "action": "store_true", + "exclusive_group": "group2", + }, + { + "name": ["--minor"], + "help": "get just the minor version. Need to be used with --project or --verbose.", + "action": "store_true", + "exclusive_group": "group2", + }, ], }, ], }, } -original_excepthook = sys.excepthook - def commitizen_excepthook( - type, value, traceback, debug=False, no_raise: list[int] | None = None -): + type: type[BaseException], + value: BaseException, + traceback: TracebackType | None, + debug: bool = False, + no_raise: list[int] | None = None, +) -> None: traceback = traceback if isinstance(traceback, TracebackType) else None - if not no_raise: - no_raise = [] - if isinstance(value, CommitizenException): - if value.message: - value.output_method(value.message) - if debug: - original_excepthook(type, value, traceback) - exit_code = value.exit_code - if exit_code in no_raise: - exit_code = ExitCode.EXPECTED_EXIT - sys.exit(exit_code) - else: - original_excepthook(type, value, traceback) - + if not isinstance(value, CommitizenException): + sys.__excepthook__(type, value, traceback) + return -commitizen_debug_excepthook = partial(commitizen_excepthook, debug=True) - -sys.excepthook = commitizen_excepthook + if value.message: + value.output_method(value.message) + if debug: + sys.__excepthook__(type, value, traceback) + exit_code = value.exit_code + if no_raise is not None and exit_code in no_raise: + sys.exit(ExitCode.EXPECTED_EXIT) + sys.exit(exit_code) def parse_no_raise(comma_separated_no_raise: str) -> list[int]: @@ -576,24 +588,48 @@ def parse_no_raise(comma_separated_no_raise: str) -> list[int]: Receives digits and strings and outputs the parsed integer which represents the exit code found in exceptions. """ - no_raise_items: list[str] = comma_separated_no_raise.split(",") - no_raise_codes = [] - for item in no_raise_items: - if item.isdecimal(): - no_raise_codes.append(int(item)) - continue - try: - exit_code = ExitCode[item.strip()] - except KeyError: - out.warn(f"WARN: no_raise key `{item}` does not exist. Skipping.") - continue - else: - no_raise_codes.append(exit_code.value) - return no_raise_codes - -def main(): - parser = cli(data) + def exit_code_from_str_or_skip(s: str) -> ExitCode | None: + try: + return ExitCode.from_str(s) + except (KeyError, ValueError): + out.warn(f"WARN: no_raise value `{s}` is not a valid exit code. Skipping.") + return None + + return [ + code.value + for s in comma_separated_no_raise.split(",") + if (code := exit_code_from_str_or_skip(s)) is not None + ] + + +if TYPE_CHECKING: + + class Args(argparse.Namespace): + config: str | None = None + debug: bool = False + name: str | None = None + no_raise: str | None = None # comma-separated string, later parsed as list[int] + report: bool = False + project: bool = False + commitizen: bool = False + verbose: bool = False + func: type[ + commands.Init # init + | commands.Commit # commit (c) + | commands.ListCz # ls + | commands.Example # example + | commands.Info # info + | commands.Schema # schema + | commands.Bump # bump + | commands.Changelog # changelog (ch) + | commands.Check # check + | commands.Version # version + ] + + +def main() -> None: + parser: argparse.ArgumentParser = cli(data) argcomplete.autocomplete(parser) # Show help if no arg provided if len(sys.argv) == 1: @@ -603,11 +639,8 @@ def main(): # This is for the command required constraint in 2.0 try: args, unknown_args = parser.parse_known_args() - except (TypeError, SystemExit) as e: - # https://github.com/commitizen-tools/commitizen/issues/429 - # argparse raises TypeError when non exist command is provided on Python < 3.9 - # but raise SystemExit with exit code == 2 on Python 3.9 - if isinstance(e, TypeError) or (isinstance(e, SystemExit) and e.code == 2): + except SystemExit as e: + if e.code == 2: raise NoCommandFoundError() raise e @@ -633,27 +666,21 @@ def main(): extra_args = " ".join(unknown_args[1:]) arguments["extra_cli_args"] = extra_args - if args.config: - conf = config.read_cfg(args.config) - else: - conf = config.read_cfg() - + conf = config.read_cfg(args.config) + args = cast("Args", args) if args.name: conf.update({"name": args.name}) - elif not args.name and not conf.path: + elif not conf.path: conf.update({"name": "cz_conventional_commits"}) + sys.excepthook = commitizen_excepthook if args.debug: logging.getLogger("commitizen").setLevel(logging.DEBUG) - sys.excepthook = commitizen_debug_excepthook - elif args.no_raise: - no_raise_exit_codes = parse_no_raise(args.no_raise) - no_raise_debug_excepthook = partial( - commitizen_excepthook, no_raise=no_raise_exit_codes - ) - sys.excepthook = no_raise_debug_excepthook - - args.func(conf, arguments)() + sys.excepthook = partial(sys.excepthook, debug=True) + if args.no_raise: + sys.excepthook = partial(sys.excepthook, no_raise=parse_no_raise(args.no_raise)) + + args.func(conf, arguments)() # type: ignore[arg-type] if __name__ == "__main__": diff --git a/commitizen/cmd.py b/commitizen/cmd.py index ba48ac7881..fe70da9c9b 100644 --- a/commitizen/cmd.py +++ b/commitizen/cmd.py @@ -1,11 +1,16 @@ +from __future__ import annotations + import os import subprocess -from typing import NamedTuple +from typing import TYPE_CHECKING, NamedTuple from charset_normalizer import from_bytes from commitizen.exceptions import CharacterSetDecodeError +if TYPE_CHECKING: + from collections.abc import Mapping + class Command(NamedTuple): out: str @@ -19,16 +24,18 @@ def _try_decode(bytes_: bytes) -> str: try: return bytes_.decode("utf-8") except UnicodeDecodeError: - charset_match = from_bytes(bytes_).best() - if charset_match is None: - raise CharacterSetDecodeError() - try: - return bytes_.decode(charset_match.encoding) - except UnicodeDecodeError as e: - raise CharacterSetDecodeError() from e + pass + + charset_match = from_bytes(bytes_).best() + if charset_match is None: + raise CharacterSetDecodeError() + try: + return bytes_.decode(charset_match.encoding) + except UnicodeDecodeError as e: + raise CharacterSetDecodeError() from e -def run(cmd: str, env=None) -> Command: +def run(cmd: str, env: Mapping[str, str] | None = None) -> Command: if env is not None: env = {**os.environ, **env} process = subprocess.Popen( diff --git a/commitizen/commands/__init__.py b/commitizen/commands/__init__.py index 806e384522..58b18297dc 100644 --- a/commitizen/commands/__init__.py +++ b/commitizen/commands/__init__.py @@ -11,13 +11,13 @@ __all__ = ( "Bump", + "Changelog", "Check", "Commit", - "Changelog", "Example", "Info", + "Init", "ListCz", "Schema", "Version", - "Init", ) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 60853094f9..0ed5ffd5b8 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -2,28 +2,25 @@ import warnings from logging import getLogger -from typing import cast +from typing import TYPE_CHECKING, cast import questionary from commitizen import bump, factory, git, hooks, out from commitizen.changelog_formats import get_changelog_format from commitizen.commands.changelog import Changelog -from commitizen.config import BaseConfig from commitizen.defaults import Settings from commitizen.exceptions import ( BumpCommitFailedError, BumpTagFailedError, DryRunExit, ExpectedExit, - GetNextExit, InvalidManualVersion, NoCommitsFoundError, NoneIncrementExit, NoPatternMapError, NotAGitProjectError, NotAllowed, - NoVersionSpecifiedError, ) from commitizen.providers import get_provider from commitizen.tags import TagRules @@ -31,46 +28,74 @@ Increment, InvalidVersion, Prerelease, + VersionProtocol, get_version_scheme, ) +if TYPE_CHECKING: + from commitizen.config import BaseConfig + logger = getLogger("commitizen") +class BumpArgs(Settings, total=False): + allow_no_commit: bool | None + annotated_tag_message: str | None + build_metadata: str | None + changelog_to_stdout: bool + changelog: bool + check_consistency: bool + devrelease: int | None + dry_run: bool + file_name: str + files_only: bool | None + get_next: bool # TODO: maybe rename to `next_version_to_stdout` + git_output_to_stderr: bool + increment_mode: str + increment: Increment | None + local_version: bool + manual_version: str | None + no_verify: bool + prerelease: Prerelease | None + retry: bool + yes: bool + + class Bump: """Show prompt for the user to create a guided commit.""" - def __init__(self, config: BaseConfig, arguments: dict): + def __init__(self, config: BaseConfig, arguments: BumpArgs) -> None: if not git.is_git_project(): raise NotAGitProjectError() self.config: BaseConfig = config - self.encoding = config.settings["encoding"] - self.arguments: dict = arguments - self.bump_settings: dict = { - **config.settings, - **{ - key: arguments[key] - for key in [ - "tag_format", - "prerelease", - "increment", - "increment_mode", - "bump_message", - "gpg_sign", - "annotated_tag", - "annotated_tag_message", - "major_version_zero", - "prerelease_offset", - "template", - "file_name", - ] - if arguments[key] is not None + self.arguments = arguments + self.bump_settings = cast( + "BumpArgs", + { + **config.settings, + **{ + k: v + for k in ( + "annotated_tag_message", + "annotated_tag", + "bump_message", + "file_name", + "gpg_sign", + "increment_mode", + "increment", + "major_version_zero", + "prerelease_offset", + "prerelease", + "tag_format", + "template", + ) + if (v := arguments.get(k)) 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"] self.git_output_to_stderr = arguments["git_output_to_stderr"] self.no_verify = arguments["no_verify"] @@ -82,7 +107,7 @@ def __init__(self, config: BaseConfig, arguments: dict): if deprecated_version_type: warnings.warn( DeprecationWarning( - "`--version-type` parameter is deprecated and will be removed in commitizen 4. " + "`--version-type` parameter is deprecated and will be removed in v5. " "Please use `--version-scheme` instead" ) ) @@ -101,31 +126,29 @@ def __init__(self, config: BaseConfig, arguments: dict): ) self.extras = arguments["extras"] - def is_initial_tag( + def _is_initial_tag( self, current_tag: git.GitTag | None, is_yes: bool = False ) -> bool: """Check if reading the whole git tree up to HEAD is needed.""" - is_initial = False - if not current_tag: - if is_yes: - is_initial = True - else: - out.info("No tag matching configuration could not be found.") - out.info( - "Possible causes:\n" - "- version in configuration is not the current version\n" - "- tag_format or legacy_tag_formats is missing, check them using 'git tag --list'\n" - ) - is_initial = questionary.confirm("Is this the first tag created?").ask() - return is_initial + if current_tag: + return False + if is_yes: + return True + + out.info("No tag matching configuration could be found.") + out.info( + "Possible causes:\n" + "- version in configuration is not the current version\n" + "- tag_format or legacy_tag_formats is missing, check them using 'git tag --list'\n" + ) + return bool(questionary.confirm("Is this the first tag created?").ask()) - def find_increment(self, commits: list[git.GitCommit]) -> Increment | None: + def _find_increment(self, commits: list[git.GitCommit]) -> Increment | None: # Update the bump map to ensure major version doesn't increment. - is_major_version_zero: bool = self.bump_settings["major_version_zero"] # self.cz.bump_map = defaults.bump_map_major_version_zero bump_map = ( self.cz.bump_map_major_version_zero - if is_major_version_zero + if self.bump_settings["major_version_zero"] else self.cz.bump_map ) bump_pattern = self.cz.bump_pattern @@ -134,171 +157,136 @@ def find_increment(self, commits: list[git.GitCommit]) -> Increment | None: raise NoPatternMapError( f"'{self.config.settings['name']}' rule does not support bump" ) - increment = bump.find_increment( - commits, regex=bump_pattern, increments_map=bump_map - ) - return increment - - def __call__(self) -> None: # noqa: C901 - """Steps executed to bump.""" - provider = get_provider(self.config) - - try: - current_version = self.scheme(provider.get_version()) - except TypeError: - raise NoVersionSpecifiedError() - - bump_commit_message: str = self.bump_settings["bump_message"] - version_files: list[str] = self.bump_settings["version_files"] - major_version_zero: bool = self.bump_settings["major_version_zero"] - prerelease_offset: int = self.bump_settings["prerelease_offset"] - - dry_run: bool = self.arguments["dry_run"] - is_yes: bool = self.arguments["yes"] - increment: Increment | None = self.arguments["increment"] - prerelease: Prerelease | None = self.arguments["prerelease"] - devrelease: int | None = self.arguments["devrelease"] - is_files_only: bool | None = self.arguments["files_only"] - is_local_version: bool = self.arguments["local_version"] - manual_version = self.arguments["manual_version"] - build_metadata = self.arguments["build_metadata"] - increment_mode: str = self.arguments["increment_mode"] - get_next: bool = self.arguments["get_next"] - allow_no_commit: bool | None = self.arguments["allow_no_commit"] - - if manual_version: - if increment: - raise NotAllowed("--increment cannot be combined with MANUAL_VERSION") - - if prerelease: - raise NotAllowed("--prerelease cannot be combined with MANUAL_VERSION") - - if devrelease is not None: - raise NotAllowed("--devrelease cannot be combined with MANUAL_VERSION") - - if is_local_version: - raise NotAllowed( - "--local-version cannot be combined with MANUAL_VERSION" - ) - - if build_metadata: - raise NotAllowed( - "--build-metadata cannot be combined with MANUAL_VERSION" - ) - - if major_version_zero: - raise NotAllowed( - "--major-version-zero cannot be combined with MANUAL_VERSION" - ) - - if get_next: - raise NotAllowed("--get-next cannot be combined with MANUAL_VERSION") - - if major_version_zero: - if not current_version.release[0] == 0: - raise NotAllowed( - f"--major-version-zero is meaningless for current version {current_version}" - ) - - if build_metadata: - if is_local_version: - raise NotAllowed( - "--local-version cannot be combined with --build-metadata" - ) - - if get_next: - # if trying to use --get-next, we should not allow --changelog or --changelog-to-stdout - if self.changelog_flag or bool(self.changelog_to_stdout): - raise NotAllowed( - "--changelog or --changelog-to-stdout is not allowed with --get-next" - ) - # --get-next is a special case, taking precedence over config for 'update_changelog_on_bump' - self.changelog_config = False - # Setting dry_run to prevent any unwanted changes to the repo or files - self.dry_run = True - else: - # If user specified changelog_to_stdout, they probably want the - # changelog to be generated as well, this is the most intuitive solution - self.changelog_flag = ( - self.changelog_flag - or bool(self.changelog_to_stdout) - or self.changelog_config + return bump.find_increment(commits, regex=bump_pattern, increments_map=bump_map) + + def _validate_arguments(self, current_version: VersionProtocol) -> None: + errors: list[str] = [] + if self.arguments["manual_version"]: + for val, option in ( + (self.arguments["increment"], "--increment"), + (self.arguments["prerelease"], "--prerelease"), + (self.arguments["devrelease"] is not None, "--devrelease"), + (self.arguments["local_version"], "--local-version"), + (self.arguments["build_metadata"], "--build-metadata"), + (self.arguments["major_version_zero"], "--major-version-zero"), + ): + if val: + errors.append(f"{option} cannot be combined with MANUAL_VERSION") + + if self.arguments["major_version_zero"] and current_version.release[0]: + errors.append( + f"--major-version-zero is meaningless for current version {current_version}" ) + if self.arguments["build_metadata"] and self.arguments["local_version"]: + errors.append("--local-version cannot be combined with --build-metadata") - rules = TagRules.from_settings(cast(Settings, self.bump_settings)) - current_tag = rules.find_tag_for(git.get_tags(), current_version) - current_tag_version = getattr( - current_tag, "name", rules.normalize_tag(current_version) - ) - - is_initial = self.is_initial_tag(current_tag, is_yes) + if errors: + raise NotAllowed("\n".join(errors)) - if manual_version: + def _resolve_increment_and_new_version( + self, current_version: VersionProtocol, current_tag: git.GitTag | None + ) -> tuple[Increment | None, VersionProtocol]: + increment = self.arguments["increment"] + if manual_version := self.arguments["manual_version"]: try: - new_version = self.scheme(manual_version) + return increment, self.scheme(manual_version) except InvalidVersion as exc: raise InvalidManualVersion( "[INVALID_MANUAL_VERSION]\n" f"Invalid manual version: '{manual_version}'" ) from exc - else: - if increment is None: - if current_tag: - commits = git.get_commits(current_tag.name) - else: - commits = git.get_commits() - - # No commits, there is no need to create an empty tag. - # Unless we previously had a prerelease. - if ( - not commits - and not current_version.is_prerelease - and not allow_no_commit - ): - raise NoCommitsFoundError( - "[NO_COMMITS_FOUND]\nNo new commits found." - ) - increment = self.find_increment(commits) + if increment is None: + commits = git.get_commits(current_tag.name if current_tag else None) + + # No commits, there is no need to create an empty tag. + # Unless we previously had a prerelease. + if ( + not commits + and not current_version.is_prerelease + and not self.arguments["allow_no_commit"] + ): + raise NoCommitsFoundError("[NO_COMMITS_FOUND]\nNo new commits found.") + + increment = self._find_increment(commits) + + # It may happen that there are commits, but they are not eligible + # for an increment, this generates a problem when using prerelease (#281) + if ( + self.arguments["prerelease"] + and increment is None + and not current_version.is_prerelease + ): + raise NoCommitsFoundError( + "[NO_COMMITS_FOUND]\n" + "No commits found to generate a pre-release.\n" + "To avoid this error, manually specify the type of increment with `--increment`" + ) - # It may happen that there are commits, but they are not eligible - # for an increment, this generates a problem when using prerelease (#281) - if prerelease and increment is None and not current_version.is_prerelease: - raise NoCommitsFoundError( - "[NO_COMMITS_FOUND]\n" - "No commits found to generate a pre-release.\n" - "To avoid this error, manually specify the type of increment with `--increment`" - ) + # we create an empty PATCH increment for empty tag + if increment is None and self.arguments["allow_no_commit"]: + increment = "PATCH" + + return increment, current_version.bump( + increment, + prerelease=self.arguments["prerelease"], + prerelease_offset=self.bump_settings["prerelease_offset"], + devrelease=self.arguments["devrelease"], + is_local_version=self.arguments["local_version"], + build_metadata=self.arguments["build_metadata"], + exact_increment=self.arguments["increment_mode"] == "exact", + ) - # we create an empty PATCH increment for empty tag - if increment is None and allow_no_commit: - increment = "PATCH" - - new_version = current_version.bump( - increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, - is_local_version=is_local_version, - build_metadata=build_metadata, - exact_increment=increment_mode == "exact", + def __call__(self) -> None: + """Steps executed to bump.""" + provider = get_provider(self.config) + current_version = self.scheme(provider.get_version()) + self._validate_arguments(current_version) + + next_version_to_stdout = self.arguments["get_next"] + if next_version_to_stdout: + for value, option in ( + (self.changelog_flag, "--changelog"), + (self.changelog_to_stdout, "--changelog-to-stdout"), + ): + if value: + warnings.warn(f"{option} has no effect when used with --get-next") + + # If user specified changelog_to_stdout, they probably want the + # changelog to be generated as well, this is the most intuitive solution + self.changelog_flag = any( + ( + self.changelog_flag, + self.changelog_to_stdout, + self.config.settings.get("update_changelog_on_bump"), ) + ) - new_tag_version = rules.normalize_tag(new_version) - message = bump.create_commit_message( - current_version, new_version, bump_commit_message + rules = TagRules.from_settings(cast("Settings", self.bump_settings)) + current_tag = rules.find_tag_for(git.get_tags(), current_version) + current_tag_version = ( + current_tag.name if current_tag else rules.normalize_tag(current_version) ) - if get_next: + is_initial = self._is_initial_tag(current_tag, self.arguments["yes"]) + + increment, new_version = self._resolve_increment_and_new_version( + current_version, current_tag + ) + + new_tag_version = rules.normalize_tag(new_version) + if next_version_to_stdout: if increment is None and new_tag_version == current_tag_version: raise NoneIncrementExit( "[NO_COMMITS_TO_BUMP]\n" "The commits found are not eligible to be bumped" ) - out.write(str(new_version)) - raise GetNextExit() + raise DryRunExit() + message = bump.create_commit_message( + current_version, new_version, self.bump_settings["bump_message"] + ) # Report found information information = f"{message}\ntag to create: {new_tag_version}\n" if increment: @@ -317,9 +305,10 @@ def __call__(self) -> None: # noqa: C901 "[NO_COMMITS_TO_BUMP]\nThe commits found are not eligible to be bumped" ) - files: list[str] = [] + updated_files: list[str] = [] + dry_run = self.arguments["dry_run"] if self.changelog_flag: - args = { + changelog_args = { "unreleased_version": new_tag_version, "template": self.template, "extras": self.extras, @@ -327,28 +316,33 @@ def __call__(self) -> None: # noqa: C901 "dry_run": dry_run, } if self.changelog_to_stdout: - changelog_cmd = Changelog(self.config, {**args, "dry_run": True}) + changelog_cmd = Changelog( + self.config, + {**changelog_args, "dry_run": True}, # type: ignore[typeddict-item] + ) try: changelog_cmd() except DryRunExit: pass - args["file_name"] = self.file_name - changelog_cmd = Changelog(self.config, args) + changelog_cmd = Changelog( + self.config, + {**changelog_args, "file_name": self.file_name}, # type: ignore[typeddict-item] + ) changelog_cmd() - files.append(changelog_cmd.file_name) + updated_files.append(changelog_cmd.file_name) # Do not perform operations over files or git. if dry_run: raise DryRunExit() - files.extend( + updated_files.extend( bump.update_version_in_files( str(current_version), str(new_version), - version_files, + self.bump_settings["version_files"], check_consistency=self.check_consistency, - encoding=self.encoding, + encoding=self.config.settings["encoding"], ) ) @@ -370,40 +364,42 @@ def __call__(self) -> None: # noqa: C901 else None, ) - if is_files_only: + if self.arguments["files_only"]: raise ExpectedExit() # FIXME: check if any changes have been staged - git.add(*files) + git.add(*updated_files) c = git.commit(message, args=self._get_commit_args()) if self.retry and c.return_code != 0 and self.changelog_flag: # Maybe pre-commit reformatted some files? Retry once logger.debug("1st git.commit error: %s", c.err) logger.info("1st commit attempt failed; retrying once") - git.add(*files) + git.add(*updated_files) c = git.commit(message, args=self._get_commit_args()) if c.return_code != 0: err = c.err.strip() or c.out raise BumpCommitFailedError(f'2nd git.commit error: "{err}"') - if c.out: - if self.git_output_to_stderr: - out.diagnostic(c.out) - else: - out.write(c.out) - if c.err: - if self.git_output_to_stderr: - out.diagnostic(c.err) - else: - out.write(c.err) + for msg in (c.out, c.err): + if msg: + out_func = out.diagnostic if self.git_output_to_stderr else out.write + out_func(msg) c = git.tag( new_tag_version, - signed=self.bump_settings.get("gpg_sign", False) - or bool(self.config.settings.get("gpg_sign", False)), - annotated=self.bump_settings.get("annotated_tag", False) - or bool(self.config.settings.get("annotated_tag", False)) - or bool(self.bump_settings.get("annotated_tag_message", False)), + signed=any( + ( + self.bump_settings.get("gpg_sign"), + self.config.settings.get("gpg_sign"), + ) + ), + annotated=any( + ( + self.bump_settings.get("annotated_tag"), + self.config.settings.get("annotated_tag"), + self.bump_settings.get("annotated_tag_message"), + ) + ), msg=self.bump_settings.get("annotated_tag_message", None), # TODO: also get from self.config.settings? ) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 3b8f43e372..215e03206a 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -5,12 +5,10 @@ from difflib import SequenceMatcher from operator import itemgetter from pathlib import Path -from typing import Callable, cast +from typing import TYPE_CHECKING, Any, TypedDict, cast from commitizen import changelog, defaults, factory, git, out from commitizen.changelog_formats import get_changelog_format -from commitizen.config import BaseConfig -from commitizen.cz.base import ChangelogReleaseHook, MessageBuilderHook from commitizen.cz.utils import strip_local_version from commitizen.exceptions import ( DryRunExit, @@ -24,17 +22,41 @@ from commitizen.tags import TagRules from commitizen.version_schemes import get_version_scheme +if TYPE_CHECKING: + from collections.abc import Generator, Iterable + + from commitizen.config import BaseConfig + + +class ChangelogArgs(TypedDict, total=False): + change_type_map: dict[str, str] + change_type_order: list[str] + current_version: str + dry_run: bool + file_name: str + incremental: bool + merge_prerelease: bool + rev_range: str + start_rev: str + tag_format: str + unreleased_version: str | None + version_scheme: str + template: str + extras: dict[str, Any] + export_template: str + class Changelog: """Generate a changelog based on the commit history.""" - def __init__(self, config: BaseConfig, args): + def __init__(self, config: BaseConfig, arguments: ChangelogArgs) -> None: if not git.is_git_project(): raise NotAGitProjectError() - self.config: BaseConfig = config - changelog_file_name = args.get("file_name") or cast( - str, self.config.settings.get("changelog_file") + self.config = config + + changelog_file_name = arguments.get("file_name") or self.config.settings.get( + "changelog_file" ) if not isinstance(changelog_file_name, str): raise NotAllowed( @@ -48,60 +70,61 @@ def __init__(self, config: BaseConfig, args): else changelog_file_name ) - 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( + self.start_rev = arguments.get("start_rev") or self.config.settings.get( "changelog_start_rev" ) self.changelog_format = get_changelog_format(self.config, self.file_name) - self.incremental = args["incremental"] or self.config.settings.get( - "changelog_incremental" + self.incremental = bool( + arguments.get("incremental") + or self.config.settings.get("changelog_incremental") ) - self.dry_run = args["dry_run"] + self.dry_run = bool(arguments.get("dry_run")) self.scheme = get_version_scheme( - self.config.settings, args.get("version_scheme") + self.config.settings, arguments.get("version_scheme") ) current_version = ( - args.get("current_version", config.settings.get("version")) or "" + arguments.get("current_version") + or self.config.settings.get("version") + or "" ) self.current_version = self.scheme(current_version) if current_version else None - self.unreleased_version = args["unreleased_version"] + self.unreleased_version = arguments["unreleased_version"] self.change_type_map = ( self.config.settings.get("change_type_map") or self.cz.change_type_map ) - self.change_type_order = ( + self.change_type_order = cast( + "list[str]", self.config.settings.get("change_type_order") or self.cz.change_type_order - or defaults.change_type_order - ) - self.rev_range = args.get("rev_range") - self.tag_format: str = ( - args.get("tag_format") or self.config.settings["tag_format"] + or defaults.CHANGE_TYPE_ORDER, ) + self.rev_range = arguments.get("rev_range") self.tag_rules = TagRules( scheme=self.scheme, - tag_format=self.tag_format, + tag_format=arguments.get("tag_format") + or self.config.settings["tag_format"], legacy_tag_formats=self.config.settings["legacy_tag_formats"], ignored_tag_formats=self.config.settings["ignored_tag_formats"], - merge_prereleases=args.get("merge_prerelease") + merge_prereleases=arguments.get("merge_prerelease") or self.config.settings["changelog_merge_prerelease"], ) self.template = ( - args.get("template") + arguments.get("template") or self.config.settings.get("template") or self.changelog_format.template ) - self.extras = args.get("extras") or {} - self.export_template_to = args.get("export_template") + self.extras = arguments.get("extras") or {} + self.export_template_to = arguments.get("export_template") - def _find_incremental_rev(self, latest_version: str, tags: list[GitTag]) -> str: + def _find_incremental_rev(self, latest_version: str, tags: Iterable[GitTag]) -> str: """Try to find the 'start_rev'. We use a similarity approach. We know how to parse the version from the markdown @@ -114,29 +137,31 @@ def _find_incremental_rev(self, latest_version: str, tags: list[GitTag]) -> str: on our experience. """ SIMILARITY_THRESHOLD = 0.89 - tag_ratio = map( - lambda tag: ( - SequenceMatcher( + scores_and_tag_names: Generator[tuple[float, str]] = ( + ( + score, + tag.name, + ) + for tag in tags + if ( + score := SequenceMatcher( None, latest_version, strip_local_version(tag.name) - ).ratio(), - tag, - ), - tags, + ).ratio() + ) + >= SIMILARITY_THRESHOLD ) try: - score, tag = max(tag_ratio, key=itemgetter(0)) + _, start_rev = max(scores_and_tag_names, key=itemgetter(0)) except ValueError: raise NoRevisionError() - if score < SIMILARITY_THRESHOLD: - raise NoRevisionError() - start_rev = tag.name return start_rev - def write_changelog( + def _write_changelog( self, changelog_out: str, lines: list[str], changelog_meta: changelog.Metadata - ): - changelog_hook: Callable | None = self.cz.changelog_hook - with smart_open(self.file_name, "w", encoding=self.encoding) as changelog_file: + ) -> None: + with smart_open( + self.file_name, "w", encoding=self.config.settings["encoding"] + ) as changelog_file: partial_changelog: str | None = None if self.incremental: new_lines = changelog.incremental_build( @@ -145,32 +170,28 @@ def write_changelog( changelog_out = "".join(new_lines) partial_changelog = changelog_out - if changelog_hook: - changelog_out = changelog_hook(changelog_out, partial_changelog) + if self.cz.changelog_hook: + changelog_out = self.cz.changelog_hook(changelog_out, partial_changelog) changelog_file.write(changelog_out) - def export_template(self): - tpl = changelog.get_changelog_template(self.cz.template_loader, self.template) - src = Path(tpl.filename) - Path(self.export_template_to).write_text(src.read_text()) + def _export_template(self, dist: str) -> None: + filename = changelog.get_changelog_template( + self.cz.template_loader, self.template + ).filename + if filename is None: + raise NotAllowed("Template filename is not set") + + text = Path(filename).read_text() + Path(dist).write_text(text) - def __call__(self): + def __call__(self) -> None: commit_parser = self.cz.commit_parser changelog_pattern = self.cz.changelog_pattern start_rev = self.start_rev - unreleased_version = self.unreleased_version - changelog_meta = changelog.Metadata() - change_type_map: dict | None = self.change_type_map - changelog_message_builder_hook: MessageBuilderHook | None = ( - self.cz.changelog_message_builder_hook - ) - changelog_release_hook: ChangelogReleaseHook | None = ( - self.cz.changelog_release_hook - ) if self.export_template_to: - return self.export_template() + return self._export_template(self.export_template_to) if not changelog_pattern or not commit_parser: raise NoPatternMapError( @@ -181,60 +202,71 @@ def __call__(self): raise NotAllowed("--incremental cannot be combined with a rev_range") # Don't continue if no `file_name` specified. - assert self.file_name + if not self.file_name: + raise NotAllowed("filename is required.") tags = self.tag_rules.get_version_tags(git.get_tags(), warn=True) - end_rev = "" + changelog_meta = changelog.Metadata() if self.incremental: changelog_meta = self.changelog_format.get_metadata(self.file_name) if changelog_meta.latest_version: start_rev = self._find_incremental_rev( - strip_local_version(changelog_meta.latest_version_tag), tags + strip_local_version(changelog_meta.latest_version_tag or ""), tags ) + + end_rev = "" if self.rev_range: start_rev, end_rev = changelog.get_oldest_and_newest_rev( tags, self.rev_range, self.tag_rules, ) + commits = git.get_commits(start=start_rev, end=end_rev, args="--topo-order") if not commits and ( self.current_version is None or not self.current_version.is_prerelease ): raise NoCommitsFoundError("No commits found") + tree = changelog.generate_tree_from_commits( commits, tags, commit_parser, changelog_pattern, - unreleased_version, - change_type_map=change_type_map, - changelog_message_builder_hook=changelog_message_builder_hook, - changelog_release_hook=changelog_release_hook, + self.unreleased_version, + change_type_map=self.change_type_map, + changelog_message_builder_hook=self.cz.changelog_message_builder_hook, + changelog_release_hook=self.cz.changelog_release_hook, rules=self.tag_rules, ) if self.change_type_order: - tree = changelog.order_changelog_tree(tree, self.change_type_order) + tree = changelog.generate_ordered_changelog_tree( + tree, self.change_type_order + ) - extras = self.cz.template_extras.copy() - extras.update(self.config.settings["extras"]) - extras.update(self.extras) changelog_out = changelog.render_changelog( - tree, loader=self.cz.template_loader, template=self.template, **extras - ) - changelog_out = changelog_out.lstrip("\n") + tree, + self.cz.template_loader, + self.template, + **{ + **self.cz.template_extras, + **self.config.settings["extras"], + **self.extras, + }, + ).lstrip("\n") # Dry_run is executed here to avoid checking and reading the files if self.dry_run: - changelog_hook: Callable | None = self.cz.changelog_hook - if changelog_hook: - changelog_out = changelog_hook(changelog_out, "") + if self.cz.changelog_hook: + changelog_out = self.cz.changelog_hook(changelog_out, "") out.write(changelog_out) raise DryRunExit() lines = [] if self.incremental and os.path.isfile(self.file_name): - with open(self.file_name, encoding=self.encoding) as changelog_file: + with open( + self.file_name, encoding=self.config.settings["encoding"] + ) as changelog_file: lines = changelog_file.readlines() - self.write_changelog(changelog_out, lines, changelog_meta) + self._write_changelog(changelog_out, lines, changelog_meta) diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index e22155cf78..0ea5885eba 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -1,23 +1,36 @@ from __future__ import annotations -import os import re import sys -from typing import Any +from typing import TYPE_CHECKING, TypedDict from commitizen import factory, git, out -from commitizen.config import BaseConfig from commitizen.exceptions import ( + CommitMessageLengthExceededError, InvalidCommandArgumentError, InvalidCommitMessageError, NoCommitsFoundError, ) +if TYPE_CHECKING: + from commitizen.config import BaseConfig + + +class CheckArgs(TypedDict, total=False): + commit_msg_file: str + commit_msg: str + rev_range: str + allow_abort: bool + message_length_limit: int | None + allowed_prefixes: list[str] + message: str + use_default_range: bool + class Check: """Check if the current commit msg matches the commitizen format.""" - def __init__(self, config: BaseConfig, arguments: dict[str, Any], cwd=os.getcwd()): + def __init__(self, config: BaseConfig, arguments: CheckArgs, *args: object) -> None: """Initial check command. Args: @@ -25,16 +38,19 @@ def __init__(self, config: BaseConfig, arguments: dict[str, Any], cwd=os.getcwd( arguments: All the flags provided by the user cwd: Current work directory """ - self.commit_msg_file: str | None = arguments.get("commit_msg_file") - self.commit_msg: str | None = arguments.get("message") - self.rev_range: str | None = arguments.get("rev_range") - self.allow_abort: bool = bool( + self.commit_msg_file = arguments.get("commit_msg_file") + self.commit_msg = arguments.get("message") + self.rev_range = arguments.get("rev_range") + self.allow_abort = bool( arguments.get("allow_abort", config.settings["allow_abort"]) ) - self.max_msg_length: int = arguments.get("message_length_limit", 0) - # we need to distinguish between None and [], which is a valid value + self.use_default_range = bool(arguments.get("use_default_range")) + self.max_msg_length = arguments.get( + "message_length_limit", config.settings.get("message_length_limit", None) + ) + # we need to distinguish between None and [], which is a valid value allowed_prefixes = arguments.get("allowed_prefixes") self.allowed_prefixes: list[str] = ( allowed_prefixes @@ -42,74 +58,74 @@ def __init__(self, config: BaseConfig, arguments: dict[str, Any], cwd=os.getcwd( else config.settings["allowed_prefixes"] ) - self._valid_command_argument() - - self.config: BaseConfig = config - self.encoding = config.settings["encoding"] - self.cz = factory.commiter_factory(self.config) - - def _valid_command_argument(self): num_exclusive_args_provided = sum( arg is not None - for arg in (self.commit_msg_file, self.commit_msg, self.rev_range) + for arg in ( + self.commit_msg_file, + self.commit_msg, + self.rev_range, + ) ) - if num_exclusive_args_provided == 0 and not sys.stdin.isatty(): - self.commit_msg = sys.stdin.read() - elif num_exclusive_args_provided != 1: + + if num_exclusive_args_provided > 1: raise InvalidCommandArgumentError( "Only one of --rev-range, --message, and --commit-msg-file is permitted by check command! " "See 'cz check -h' for more information" ) - def __call__(self): + if num_exclusive_args_provided == 0 and not sys.stdin.isatty(): + self.commit_msg = sys.stdin.read() + + self.config: BaseConfig = config + self.cz = factory.committer_factory(self.config) + + def __call__(self) -> None: """Validate if commit messages follows the conventional pattern. Raises: InvalidCommitMessageError: if the commit provided not follows the conventional pattern + NoCommitsFoundError: if no commit is found with the given range """ commits = self._get_commits() if not commits: raise NoCommitsFoundError(f"No commit found with range: '{self.rev_range}'") - pattern = self.cz.schema_pattern() - ill_formated_commits = [ - commit + pattern = re.compile(self.cz.schema_pattern()) + invalid_msgs_content = "\n".join( + f'commit "{commit.rev}": "{commit.message}"' 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 - ] + if not self._validate_commit_message(commit.message, pattern, commit.rev) ) - if displayed_msgs_content: + if invalid_msgs_content: + # TODO: capitalize the first letter of the error message for consistency in v5 raise InvalidCommitMessageError( "commit validation: failed!\n" "please enter a commit message in the commitizen format.\n" - f"{displayed_msgs_content}\n" - f"pattern: {pattern}" + f"{invalid_msgs_content}\n" + f"pattern: {pattern.pattern}" ) out.success("Commit validation: successful!") - 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)] + 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.config.settings["encoding"] + ) as commit_file: + # Get commit message from file (--commit-msg-file) + return commit_file.read() + + def _get_commits(self) -> list[git.GitCommit]: + 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( + git.get_default_branch() if self.use_default_range else None, + self.rev_range, + ) @staticmethod def _filter_comments(msg: str) -> str: @@ -134,7 +150,7 @@ def _filter_comments(msg: str) -> str: The filtered commit message without comments. """ - lines = [] + lines: list[str] = [] for line in msg.split("\n"): if "# ------------------------ >8 ------------------------" in line: break @@ -142,14 +158,23 @@ def _filter_comments(msg: str) -> str: lines.append(line) return "\n".join(lines) - def validate_commit_message(self, commit_msg: str, pattern: str) -> bool: + def _validate_commit_message( + self, commit_msg: str, pattern: re.Pattern[str], commit_hash: str + ) -> bool: if not commit_msg: return self.allow_abort if any(map(commit_msg.startswith, self.allowed_prefixes)): return True - if self.max_msg_length: + + if self.max_msg_length is not None: msg_len = len(commit_msg.partition("\n")[0].strip()) if msg_len > self.max_msg_length: - return False - return bool(re.match(pattern, commit_msg)) + raise CommitMessageLengthExceededError( + f"commit validation: failed!\n" + f"commit message length exceeds the limit.\n" + f'commit "{commit_hash}": "{commit_msg}"\n' + f"message length limit: {self.max_msg_length} (actual: {msg_len})" + ) + + return bool(pattern.match(commit_msg)) diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 93f048082b..3894d0b77e 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -5,11 +5,11 @@ import shutil import subprocess import tempfile +from typing import TYPE_CHECKING, TypedDict import questionary from commitizen import factory, git, out -from commitizen.config import BaseConfig from commitizen.cz.exceptions import CzException from commitizen.cz.utils import get_backup_file_path from commitizen.exceptions import ( @@ -25,56 +25,79 @@ ) from commitizen.git import smart_open +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config import BaseConfig + + +class CommitArgs(TypedDict, total=False): + all: bool + dry_run: bool + edit: bool + extra_cli_args: str + message_length_limit: int | None + no_retry: bool + signoff: bool + write_message_to_file: Path | None + retry: bool + class Commit: """Show prompt for the user to create a guided commit.""" - def __init__(self, config: BaseConfig, arguments: dict): + def __init__(self, config: BaseConfig, arguments: CommitArgs) -> None: if not git.is_git_project(): raise NotAGitProjectError() 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() + self.backup_file_path = get_backup_file_path() - def read_backup_message(self) -> str | None: + def _read_backup_message(self) -> str | None: # Check the commit backup file exists - if not os.path.isfile(self.temp_file): + if not self.backup_file_path.is_file(): return None # Read commit message from backup - with open(self.temp_file, encoding=self.encoding) as f: + with open( + self.backup_file_path, encoding=self.config.settings["encoding"] + ) as f: return f.read().strip() - def prompt_commit_questions(self) -> str: + def _get_message_by_prompt_commit_questions(self) -> str: # Prompt user for the commit message - cz = self.cz - questions = cz.questions() - for question in filter(lambda q: q["type"] == "list", questions): + questions = self.cz.questions() + for question in (q for q in questions if q["type"] == "list"): question["use_shortcuts"] = self.config.settings["use_shortcuts"] try: - answers = questionary.prompt(questions, style=cz.style) + answers = questionary.prompt(questions, style=self.cz.style) except ValueError as err: root_err = err.__context__ if isinstance(root_err, CzException): - raise CustomError(root_err.__str__()) + raise CustomError(str(root_err)) raise err if not answers: raise NoAnswersError() - message = cz.message(answers) - message_len = len(message.partition("\n")[0].strip()) - message_length_limit: int = self.arguments.get("message_length_limit", 0) - if 0 < message_length_limit < message_len: - raise CommitMessageLengthExceededError( - f"Length of commit message exceeds limit ({message_len}/{message_length_limit})" - ) + message = self.cz.message(answers) + if limit := self.arguments.get( + "message_length_limit", self.config.settings.get("message_length_limit", 0) + ): + self._validate_subject_length(message=message, length_limit=limit) return message + def _validate_subject_length(self, *, message: str, length_limit: int) -> None: + # By the contract, message_length_limit is set to 0 for no limit + subject = message.partition("\n")[0].strip() + if len(subject) > length_limit: + raise CommitMessageLengthExceededError( + f"Length of commit message exceeds limit ({len(subject)}/{length_limit}), subject: '{subject}'" + ) + def manual_edit(self, message: str) -> str: editor = git.get_core_editor() if editor is None: @@ -92,79 +115,77 @@ def manual_edit(self, message: str) -> str: os.unlink(file.name) return message - def __call__(self): - extra_args: str = self.arguments.get("extra_cli_args", "") - - allow_empty: bool = "--allow-empty" in extra_args + def _get_message(self) -> str: + if self.arguments.get("retry"): + commit_message = self._read_backup_message() + if commit_message is None: + raise NoCommitBackupError() + return commit_message + + if ( + self.config.settings.get("retry_after_failure") + and not self.arguments.get("no_retry") + and (backup_message := self._read_backup_message()) + ): + return backup_message + return self._get_message_by_prompt_commit_questions() + + def __call__(self) -> None: + extra_args = self.arguments.get("extra_cli_args", "") + dry_run = bool(self.arguments.get("dry_run")) + write_message_to_file = self.arguments.get("write_message_to_file") + signoff = bool(self.arguments.get("signoff")) - dry_run: bool = self.arguments.get("dry_run") - write_message_to_file: bool = self.arguments.get("write_message_to_file") - manual_edit: bool = self.arguments.get("edit") + if signoff: + out.warn( + "Deprecated warning: `cz commit -s` is deprecated and will be removed in v5, please use `cz commit -- -s` instead." + ) - is_all: bool = self.arguments.get("all") - if is_all: - c = git.add("-u") + if self.arguments.get("all"): + git.add("-u") - if git.is_staging_clean() and not (dry_run or allow_empty): + if git.is_staging_clean() and not (dry_run or "--allow-empty" in extra_args): 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") - no_retry: bool = self.arguments.get("no_retry") - retry_after_failure: bool = self.config.settings.get("retry_after_failure") - - if retry: - m = self.read_backup_message() - if m is None: - raise NoCommitBackupError() - elif retry_after_failure and not no_retry: - m = self.read_backup_message() - if m is None: - m = self.prompt_commit_questions() - else: - m = self.prompt_commit_questions() - - if manual_edit: - m = self.manual_edit(m) + commit_message = self._get_message() + if self.arguments.get("edit"): + commit_message = self.manual_edit(commit_message) - out.info(f"\n{m}\n") + out.info(f"\n{commit_message}\n") if write_message_to_file: - with smart_open(write_message_to_file, "w", encoding=self.encoding) as file: - file.write(m) + with smart_open( + write_message_to_file, "w", encoding=self.config.settings["encoding"] + ) as file: + file.write(commit_message) if dry_run: raise DryRunExit() - always_signoff: bool = self.config.settings["always_signoff"] - signoff: bool = self.arguments.get("signoff") - - if signoff: - out.warn( - "signoff mechanic is deprecated, please use `cz commit -- -s` instead." - ) - - if always_signoff or signoff: + if self.config.settings["always_signoff"] or signoff: extra_args = f"{extra_args} -s".strip() - c = git.commit(m, args=extra_args) - + c = git.commit(commit_message, args=extra_args) if c.return_code != 0: out.error(c.err) # Create commit backup - with smart_open(self.temp_file, "w", encoding=self.encoding) as f: - f.write(m) + with smart_open( + self.backup_file_path, "w", encoding=self.config.settings["encoding"] + ) as f: + f.write(commit_message) raise CommitError() - if "nothing added" in c.out or "no changes added to commit" in c.out: + if any(s in c.out for s in ("nothing added", "no changes added to commit")): out.error(c.out) - else: - with contextlib.suppress(FileNotFoundError): - os.remove(self.temp_file) - out.write(c.err) - out.write(c.out) - out.success("Commit successful!") + return + + with contextlib.suppress(FileNotFoundError): + self.backup_file_path.unlink() + out.write(c.err) + out.write(c.out) + out.success("Commit successful!") diff --git a/commitizen/commands/example.py b/commitizen/commands/example.py index e7abe7b318..ba9f34adc4 100644 --- a/commitizen/commands/example.py +++ b/commitizen/commands/example.py @@ -5,9 +5,9 @@ class Example: """Show an example so people understands the rules.""" - def __init__(self, config: BaseConfig, *args): + def __init__(self, config: BaseConfig, *args: object) -> None: self.config: BaseConfig = config - self.cz = factory.commiter_factory(self.config) + self.cz = factory.committer_factory(self.config) - def __call__(self): + def __call__(self) -> None: out.write(self.cz.example()) diff --git a/commitizen/commands/info.py b/commitizen/commands/info.py index afac9797e4..5ea8227313 100644 --- a/commitizen/commands/info.py +++ b/commitizen/commands/info.py @@ -5,9 +5,9 @@ class Info: """Show in depth explanation of your rules.""" - def __init__(self, config: BaseConfig, *args): + def __init__(self, config: BaseConfig, *args: object) -> None: self.config: BaseConfig = config - self.cz = factory.commiter_factory(self.config) + self.cz = factory.committer_factory(self.config) - def __call__(self): + def __call__(self) -> None: out.write(self.cz.info()) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index df872ec7ee..62678a2244 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -1,99 +1,90 @@ from __future__ import annotations -import os -import shutil -from typing import Any +from pathlib import Path +from typing import TYPE_CHECKING, Any, NamedTuple import questionary import yaml -from commitizen import cmd, factory, out +from commitizen import cmd, factory, out, project_info from commitizen.__version__ import __version__ -from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig +from commitizen.config.factory import create_config 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 +if TYPE_CHECKING: + from commitizen.config import ( + BaseConfig, + ) -class ProjectInfo: - """Discover information about the current folder.""" - @property - def has_pyproject(self) -> bool: - return os.path.isfile("pyproject.toml") - - @property - def has_uv_lock(self) -> bool: - return os.path.isfile("uv.lock") - - @property - def has_setup(self) -> bool: - return os.path.isfile("setup.py") - - @property - def has_pre_commit_config(self) -> bool: - return os.path.isfile(".pre-commit-config.yaml") - - @property - def is_python_uv(self) -> bool: - return self.has_pyproject and self.has_uv_lock - - @property - def is_python_poetry(self) -> bool: - if not self.has_pyproject: - return False - with open("pyproject.toml") as f: - return "[tool.poetry]" in f.read() - - @property - def is_python(self) -> bool: - return self.has_pyproject or self.has_setup - - @property - def is_rust_cargo(self) -> bool: - return os.path.isfile("Cargo.toml") - - @property - def is_npm_package(self) -> bool: - return os.path.isfile("package.json") - - @property - def is_php_composer(self) -> bool: - return os.path.isfile("composer.json") - - @property - def latest_tag(self) -> str | None: - return get_latest_tag_name() - - def tags(self) -> list | None: - """Not a property, only use if necessary""" - if self.latest_tag is None: - return None - return get_tag_names() +class _VersionProviderOption(NamedTuple): + provider_name: str + description: str @property - def is_pre_commit_installed(self) -> bool: - return bool(shutil.which("pre-commit")) + def title(self) -> str: + return f"{self.provider_name}: {self.description}" + + +_VERSION_PROVIDER_CHOICES = tuple( + questionary.Choice(title=option.title, value=option.provider_name) + for option in ( + _VersionProviderOption( + provider_name="commitizen", + description="Fetch and set version in commitizen config (default)", + ), + _VersionProviderOption( + provider_name="cargo", + description="Get and set version from Cargo.toml:project.version field", + ), + _VersionProviderOption( + provider_name="composer", + description="Get and set version from composer.json:project.version field", + ), + _VersionProviderOption( + provider_name="npm", + description="Get and set version from package.json:project.version field", + ), + _VersionProviderOption( + provider_name="pep621", + description="Get and set version from pyproject.toml:project.version field", + ), + _VersionProviderOption( + provider_name="poetry", + description="Get and set version from pyproject.toml:tool.poetry.version field", + ), + _VersionProviderOption( + provider_name="uv", + description="Get and set version from pyproject.toml and uv.lock", + ), + _VersionProviderOption( + provider_name="scm", + description="Fetch the version from git and does not need to set it back", + ), + ) +) class Init: - def __init__(self, config: BaseConfig, *args): + _PRE_COMMIT_CONFIG_PATH = ".pre-commit-config.yaml" + + def __init__(self, config: BaseConfig, *args: object) -> None: self.config: BaseConfig = config - self.encoding = config.settings["encoding"] - self.cz = factory.commiter_factory(self.config) - self.project_info = ProjectInfo() + self.cz = factory.committer_factory(self.config) - def __call__(self): + def __call__(self) -> None: if self.config.path: out.line(f"Config file {self.config.path} already exists") return out.info("Welcome to commitizen!\n") out.line( - "Answer the questions to configure your project.\n" - "For further configuration visit:\n" + "Answer the following questions to configure your project.\n" + "For further configuration, visit:\n" "\n" "https://commitizen-tools.github.io/commitizen/config/" "\n" @@ -110,66 +101,68 @@ def __call__(self): tag_format = self._ask_tag_format(tag) # confirm & text update_changelog_on_bump = self._ask_update_changelog_on_bump() # confirm major_version_zero = self._ask_major_version_zero(version) # confirm + hook_types: list[str] | None = questionary.checkbox( + "What types of pre-commit hook you want to install? (Leave blank if you don't want to install)", + choices=[ + questionary.Choice("commit-msg", checked=False), + questionary.Choice("pre-push", checked=False), + ], + ).unsafe_ask() except KeyboardInterrupt: raise InitFailedError("Stopped by user") - # Initialize configuration - if "toml" in config_path: - self.config = TomlConfig(data="", path=config_path) - elif "json" in config_path: - self.config = JsonConfig(data="{}", path=config_path) - elif "yaml" in config_path: - self.config = YAMLConfig(data="", path=config_path) - values_to_add = {} - values_to_add["name"] = cz_name - values_to_add["tag_format"] = tag_format - values_to_add["version_scheme"] = version_scheme - - if version_provider == "commitizen": - values_to_add["version"] = version.public - else: - values_to_add["version_provider"] = version_provider - - if update_changelog_on_bump: - values_to_add["update_changelog_on_bump"] = update_changelog_on_bump - - if major_version_zero: - values_to_add["major_version_zero"] = major_version_zero - - # Collect hook data - hook_types = questionary.checkbox( - "What types of pre-commit hook you want to install? (Leave blank if you don't want to install)", - choices=[ - questionary.Choice("commit-msg", checked=False), - questionary.Choice("pre-push", checked=False), - ], - ).unsafe_ask() if hook_types: - try: - self._install_pre_commit_hook(hook_types) - except InitFailedError as e: - raise InitFailedError(f"Failed to install pre-commit hook.\n{e}") + config_data = self._get_config_data() + with smart_open( + self._PRE_COMMIT_CONFIG_PATH, + "w", + encoding=self.config.settings["encoding"], + ) as config_file: + yaml.safe_dump(config_data, stream=config_file) - # Create and initialize config - self.config.init_empty_config_content() - self._update_config_file(values_to_add) + if not project_info.is_pre_commit_installed(): + raise InitFailedError( + "Failed to install pre-commit hook.\n" + "pre-commit is not installed in current environment." + ) + + cmd_str = "pre-commit install " + " ".join( + f"--hook-type {ty}" for ty in hook_types + ) + c = cmd.run(cmd_str) + if c.return_code != 0: + raise InitFailedError( + "Failed to install pre-commit hook.\n" + f"Error running {cmd_str}." + "Outputs are attached below:\n" + f"stdout: {c.out}\n" + f"stderr: {c.err}" + ) + out.write("commitizen pre-commit hook is now installed in your '.git'\n") + + _write_config_to_file( + path=config_path, + cz_name=cz_name, + version_provider=version_provider, + version_scheme=version_scheme, + version=version, + tag_format=tag_format, + update_changelog_on_bump=update_changelog_on_bump, + major_version_zero=major_version_zero, + ) out.write("\nYou can bump the version running:\n") out.info("\tcz bump\n") out.success("Configuration complete 🚀") - def _ask_config_path(self) -> str: - default_path = ".cz.toml" - if self.project_info.has_pyproject: - default_path = "pyproject.toml" - - name: str = questionary.select( + def _ask_config_path(self) -> Path: + filename: str = questionary.select( "Please choose a supported config file: ", - choices=config_files, - default=default_path, + choices=CONFIG_FILES, + default=project_info.get_default_config_filename(), style=self.cz.style, ).unsafe_ask() - return name + return Path(filename) def _ask_name(self) -> str: name: str = questionary.select( @@ -181,104 +174,67 @@ def _ask_name(self) -> str: return name def _ask_tag(self) -> str: - latest_tag = self.project_info.latest_tag + latest_tag = get_latest_tag_name() if not latest_tag: out.error("No Existing Tag. Set tag to v0.0.1") return "0.0.1" - is_correct_tag = questionary.confirm( + if questionary.confirm( f"Is {latest_tag} the latest tag?", style=self.cz.style, default=False + ).unsafe_ask(): + return latest_tag + + existing_tags = get_tag_names() + if not existing_tags: + out.error("No Existing Tag. Set tag to v0.0.1") + return "0.0.1" + + answer: str = questionary.select( + "Please choose the latest tag: ", + # The latest tag is most likely with the largest number. + # Thus, listing the existing_tags in reverse order makes more sense. + choices=sorted(existing_tags, reverse=True), + style=self.cz.style, ).unsafe_ask() - if not is_correct_tag: - tags = self.project_info.tags() - if not tags: - out.error("No Existing Tag. Set tag to v0.0.1") - return "0.0.1" - - # the latest tag is most likely with the largest number. Thus list the tags in reverse order makes more sense - sorted_tags = sorted(tags, reverse=True) - latest_tag = questionary.select( - "Please choose the latest tag: ", - choices=sorted_tags, - style=self.cz.style, - ).unsafe_ask() - if not latest_tag: - raise NoAnswersError("Tag is required!") - return latest_tag + if not answer: + raise NoAnswersError("Tag is required!") + return answer - def _ask_tag_format(self, latest_tag) -> str: - is_correct_format = False + def _ask_tag_format(self, latest_tag: str) -> str: if latest_tag.startswith("v"): - tag_format = r"v$version" - is_correct_format = questionary.confirm( - f'Is "{tag_format}" the correct tag format?', style=self.cz.style - ).unsafe_ask() + v_tag_format = r"v$version" + if questionary.confirm( + f'Is "{v_tag_format}" the correct tag format?', style=self.cz.style + ).unsafe_ask(): + return v_tag_format default_format = DEFAULT_SETTINGS["tag_format"] - if not is_correct_format: - tag_format = questionary.text( - f'Please enter the correct version format: (default: "{default_format}")', - style=self.cz.style, - ).unsafe_ask() + tag_format: str = questionary.text( + f'Please enter the correct version format: (default: "{default_format}")', + style=self.cz.style, + ).unsafe_ask() - if not tag_format: - tag_format = default_format - return tag_format + return tag_format or default_format def _ask_version_provider(self) -> str: """Ask for setting: version_provider""" - OPTS = { - "commitizen": "commitizen: Fetch and set version in commitizen config (default)", - "cargo": "cargo: Get and set version from Cargo.toml:project.version field", - "composer": "composer: Get and set version from composer.json:project.version field", - "npm": "npm: Get and set version from package.json:project.version field", - "pep621": "pep621: Get and set version from pyproject.toml:project.version field", - "poetry": "poetry: Get and set version from pyproject.toml:tool.poetry.version field", - "uv": "uv: Get and Get and set version from pyproject.toml and uv.lock", - "scm": "scm: Fetch the version from git and does not need to set it back", - } - - default_val = "commitizen" - if self.project_info.is_python: - if self.project_info.is_python_poetry: - default_val = "poetry" - elif self.project_info.is_python_uv: - default_val = "uv" - else: - default_val = "pep621" - elif self.project_info.is_rust_cargo: - default_val = "cargo" - elif self.project_info.is_npm_package: - default_val = "npm" - elif self.project_info.is_php_composer: - default_val = "composer" - - choices = [ - questionary.Choice(title=title, value=value) - for value, title in OPTS.items() - ] - default = next(filter(lambda x: x.value == default_val, choices)) version_provider: str = questionary.select( "Choose the source of the version:", - choices=choices, + choices=_VERSION_PROVIDER_CHOICES, style=self.cz.style, - default=default, + default=project_info.get_default_version_provider(), ).unsafe_ask() return version_provider def _ask_version_scheme(self) -> str: """Ask for setting: version_scheme""" - default = "semver" - if self.project_info.is_python: - default = "pep440" - scheme: str = questionary.select( "Choose version scheme: ", - choices=list(KNOWN_SCHEMES), + choices=KNOWN_SCHEMES, style=self.cz.style, - default=default, + default=project_info.get_default_version_scheme(), ).unsafe_ask() return scheme @@ -294,7 +250,7 @@ def _ask_major_version_zero(self, version: Version) -> bool: return major_version_zero def _ask_update_changelog_on_bump(self) -> bool: - "Ask for setting: update_changelog_on_bump" + """Ask for setting: update_changelog_on_bump""" update_changelog_on_bump: bool = questionary.confirm( "Create changelog automatically on bump", default=True, @@ -302,73 +258,59 @@ def _ask_update_changelog_on_bump(self) -> bool: ).unsafe_ask() return update_changelog_on_bump - def _exec_install_pre_commit_hook(self, hook_types: list[str]): - cmd_str = self._gen_pre_commit_cmd(hook_types) - c = cmd.run(cmd_str) - if c.return_code != 0: - err_msg = ( - f"Error running {cmd_str}." - "Outputs are attached below:\n" - f"stdout: {c.out}\n" - f"stderr: {c.err}" - ) - raise InitFailedError(err_msg) - - def _gen_pre_commit_cmd(self, hook_types: list[str]) -> str: - """Generate pre-commit command according to given hook types""" - if not hook_types: - raise ValueError("At least 1 hook type should be provided.") - cmd_str = "pre-commit install " + " ".join( - f"--hook-type {ty}" for ty in hook_types - ) - return cmd_str - - def _install_pre_commit_hook(self, hook_types: list[str] | None = None): - pre_commit_config_filename = ".pre-commit-config.yaml" - cz_hook_config = { + def _get_config_data(self) -> dict[str, Any]: + CZ_HOOK_CONFIG = { "repo": "https://github.com/commitizen-tools/commitizen", "rev": f"v{__version__}", "hooks": [ {"id": "commitizen"}, - {"id": "commitizen-branch", "stages": ["push"]}, + {"id": "commitizen-branch", "stages": ["pre-push"]}, ], } - config_data = {} - if not self.project_info.has_pre_commit_config: - # .pre-commit-config.yaml does not exist - config_data["repos"] = [cz_hook_config] - else: - with open( - pre_commit_config_filename, encoding=self.encoding - ) as config_file: - yaml_data = yaml.safe_load(config_file) - if yaml_data: - config_data = yaml_data - - if "repos" in config_data: - for pre_commit_hook in config_data["repos"]: - if "commitizen" in pre_commit_hook["repo"]: - out.write("commitizen already in pre-commit config") - break - else: - config_data["repos"].append(cz_hook_config) - else: - # .pre-commit-config.yaml exists but there's no "repos" key - config_data["repos"] = [cz_hook_config] - - with smart_open( - pre_commit_config_filename, "w", encoding=self.encoding + if not Path(".pre-commit-config.yaml").is_file(): + return {"repos": [CZ_HOOK_CONFIG]} + + with open( + self._PRE_COMMIT_CONFIG_PATH, encoding=self.config.settings["encoding"] ) as config_file: - yaml.safe_dump(config_data, stream=config_file) - - if not self.project_info.is_pre_commit_installed: - raise InitFailedError("pre-commit is not installed in current environment.") - if hook_types is None: - hook_types = ["commit-msg", "pre-push"] - self._exec_install_pre_commit_hook(hook_types) - out.write("commitizen pre-commit hook is now installed in your '.git'\n") - - def _update_config_file(self, values: dict[str, Any]): - for key, value in values.items(): - self.config.set_key(key, value) + config_data: dict[str, Any] = yaml.safe_load(config_file) or {} + + if not isinstance(repos := config_data.get("repos"), list): + # .pre-commit-config.yaml exists but there's no "repos" key + config_data["repos"] = [CZ_HOOK_CONFIG] + return config_data + + # Check if commitizen pre-commit hook is already in the config + if any("commitizen" in hook_config["repo"] for hook_config in repos): + out.write("commitizen already in pre-commit config") + else: + repos.append(CZ_HOOK_CONFIG) + return config_data + + +def _write_config_to_file( + *, + path: Path, + cz_name: str, + version_provider: str, + version_scheme: str, + version: Version, + tag_format: str, + update_changelog_on_bump: bool, + major_version_zero: bool, +) -> None: + out_config = create_config(path=path) + out_config.init_empty_config_content() + + out_config.set_key("name", cz_name) + out_config.set_key("tag_format", tag_format) + out_config.set_key("version_scheme", version_scheme) + if version_provider == "commitizen": + out_config.set_key("version", version.public) + else: + out_config.set_key("version_provider", version_provider) + if update_changelog_on_bump: + out_config.set_key("update_changelog_on_bump", update_changelog_on_bump) + if major_version_zero: + out_config.set_key("major_version_zero", major_version_zero) diff --git a/commitizen/commands/list_cz.py b/commitizen/commands/list_cz.py index 99701865af..412266f6c3 100644 --- a/commitizen/commands/list_cz.py +++ b/commitizen/commands/list_cz.py @@ -6,8 +6,8 @@ class ListCz: """List currently installed rules.""" - def __init__(self, config: BaseConfig, *args): + def __init__(self, config: BaseConfig, *args: object) -> None: self.config: BaseConfig = config - def __call__(self): + def __call__(self) -> None: out.write("\n".join(registry.keys())) diff --git a/commitizen/commands/schema.py b/commitizen/commands/schema.py index 0940648cde..a7aeb53569 100644 --- a/commitizen/commands/schema.py +++ b/commitizen/commands/schema.py @@ -5,9 +5,9 @@ class Schema: """Show structure of the rule.""" - def __init__(self, config: BaseConfig, *args): + def __init__(self, config: BaseConfig, *args: object) -> None: self.config: BaseConfig = config - self.cz = factory.commiter_factory(self.config) + self.cz = factory.committer_factory(self.config) - def __call__(self): + def __call__(self) -> None: out.write(self.cz.schema()) diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py index 45d553c710..9290e80b8f 100644 --- a/commitizen/commands/version.py +++ b/commitizen/commands/version.py @@ -1,39 +1,77 @@ import platform import sys +from typing import TypedDict from commitizen import out from commitizen.__version__ import __version__ from commitizen.config import BaseConfig +from commitizen.exceptions import NoVersionSpecifiedError, VersionSchemeUnknown from commitizen.providers import get_provider +from commitizen.version_schemes import get_version_scheme + + +class VersionArgs(TypedDict, total=False): + commitizen: bool + report: bool + project: bool + verbose: bool + major: bool + minor: bool class Version: - """Get the version of the installed commitizen or the current project.""" + """Get the version of the installed commitizen or the current project. + Precedence: + 1. report + 2. commitizen + 3. verbose, project + """ - def __init__(self, config: BaseConfig, *args): + def __init__(self, config: BaseConfig, arguments: VersionArgs) -> None: self.config: BaseConfig = config - self.parameter = args[0] - self.operating_system = platform.system() - self.python_version = sys.version + self.arguments = arguments - def __call__(self): - if self.parameter.get("report"): + def __call__(self) -> None: + if self.arguments.get("report"): out.write(f"Commitizen Version: {__version__}") - out.write(f"Python Version: {self.python_version}") - out.write(f"Operating System: {self.operating_system}") - elif self.parameter.get("project"): - version = get_provider(self.config).get_version() - if version: - out.write(f"{version}") - else: - out.error("No project information in this project.") - elif self.parameter.get("verbose"): + out.write(f"Python Version: {sys.version}") + out.write(f"Operating System: {platform.system()}") + return + + if self.arguments.get("verbose"): out.write(f"Installed Commitizen Version: {__version__}") - version = get_provider(self.config).get_version() - if version: - out.write(f"Project Version: {version}") - else: + + if not self.arguments.get("commitizen") and ( + self.arguments.get("project") or self.arguments.get("verbose") + ): + try: + version = get_provider(self.config).get_version() + except NoVersionSpecifiedError: out.error("No project information in this project.") - else: - # if no argument is given, show installed commitizen version - out.write(f"{__version__}") + return + try: + version_scheme = get_version_scheme(self.config.settings)(version) + except VersionSchemeUnknown: + out.error("Unknown version scheme.") + return + + if self.arguments.get("major"): + version = f"{version_scheme.major}" + elif self.arguments.get("minor"): + version = f"{version_scheme.minor}" + + out.write( + f"Project Version: {version}" + if self.arguments.get("verbose") + else version + ) + return + + if self.arguments.get("major") or self.arguments.get("minor"): + out.error( + "Major or minor version can only be used with --project or --verbose." + ) + return + + # If no arguments are provided, just show the installed commitizen version + out.write(__version__) diff --git a/commitizen/config/__init__.py b/commitizen/config/__init__.py index f3720bb1b3..e30f9f789c 100644 --- a/commitizen/config/__init__.py +++ b/commitizen/config/__init__.py @@ -1,58 +1,49 @@ from __future__ import annotations from pathlib import Path +from typing import TYPE_CHECKING from commitizen import defaults, git +from commitizen.config.factory import create_config from commitizen.exceptions import ConfigFileIsEmpty, ConfigFileNotFound from .base_config import BaseConfig -from .json_config import JsonConfig -from .toml_config import TomlConfig -from .yaml_config import YAMLConfig +if TYPE_CHECKING: + from collections.abc import Generator -def read_cfg(filepath: str | None = None) -> BaseConfig: - conf = BaseConfig() +def _resolve_config_paths(filepath: str | None = None) -> Generator[Path, None, None]: if filepath is not None: - if not Path(filepath).exists(): + out_path = Path(filepath) + if not out_path.exists(): raise ConfigFileNotFound() - cfg_paths = (path for path in (Path(filepath),)) - else: - git_project_root = git.find_git_project_root() - cfg_search_paths = [Path(".")] - if git_project_root: - cfg_search_paths.append(git_project_root) + yield out_path + return - cfg_paths = ( - path / Path(filename) - for path in cfg_search_paths - for filename in defaults.config_files - ) + git_project_root = git.find_git_project_root() + cfg_search_paths = [Path(".")] + if git_project_root: + cfg_search_paths.append(git_project_root) - for filename in cfg_paths: - if not filename.exists(): - continue + for path in cfg_search_paths: + for filename in defaults.CONFIG_FILES: + out_path = path / Path(filename) + if out_path.exists(): + yield out_path - _conf: TomlConfig | JsonConfig | YAMLConfig +def read_cfg(filepath: str | None = None) -> BaseConfig: + for filename in _resolve_config_paths(filepath): with open(filename, "rb") as f: data: bytes = f.read() - if "toml" in filename.suffix: - _conf = TomlConfig(data=data, path=filename) - elif "json" in filename.suffix: - _conf = JsonConfig(data=data, path=filename) - elif "yaml" in filename.suffix: - _conf = YAMLConfig(data=data, path=filename) + conf = create_config(data=data, path=filename) + if not conf.is_empty_config: + return conf - if filepath is not None and _conf.is_empty_config: + if filepath is not None: raise ConfigFileIsEmpty() - elif _conf.is_empty_config: - continue - else: - conf = _conf - break - return conf + return BaseConfig() diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py index 478691aa14..98270915d8 100644 --- a/commitizen/config/base_config.py +++ b/commitizen/config/base_config.py @@ -1,14 +1,24 @@ from __future__ import annotations from pathlib import Path +from typing import TYPE_CHECKING from commitizen.defaults import DEFAULT_SETTINGS, Settings +if TYPE_CHECKING: + import sys + + # Self is Python 3.11+ but backported in typing-extensions + if sys.version_info < (3, 11): + from typing_extensions import Self + else: + from typing import Self + class BaseConfig: - def __init__(self): + def __init__(self) -> None: + self.is_empty_config = False self._settings: Settings = DEFAULT_SETTINGS.copy() - self.encoding = self.settings["encoding"] self._path: Path | None = None @property @@ -16,22 +26,29 @@ def settings(self) -> Settings: return self._settings @property - def path(self) -> Path | None: - return self._path + def path(self) -> Path: + return self._path # type: ignore[return-value] - def set_key(self, key, value): - """Set or update a key in the conf. + @path.setter + def path(self, path: Path) -> None: + self._path = Path(path) - For now only strings are supported. - We use to update the version number. + def set_key(self, key: str, value: object) -> Self: + """Set or update a key in the config file. + + Currently, only strings are supported for the parameter key. """ raise NotImplementedError() def update(self, data: Settings) -> None: self._settings.update(data) - def add_path(self, path: str | Path) -> None: - self._path = Path(path) - def _parse_setting(self, data: bytes | str) -> None: raise NotImplementedError() + + def init_empty_config_content(self) -> None: + """Create a config file with the empty config content. + + The implementation is different for each config file type. + """ + raise NotImplementedError() diff --git a/commitizen/config/factory.py b/commitizen/config/factory.py new file mode 100644 index 0000000000..d0a212b73f --- /dev/null +++ b/commitizen/config/factory.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from commitizen.config.json_config import JsonConfig +from commitizen.config.toml_config import TomlConfig +from commitizen.config.yaml_config import YAMLConfig + +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig + + +def create_config(*, data: bytes | str | None = None, path: Path) -> BaseConfig: + if "toml" in path.suffix: + return TomlConfig(data=data or "", path=path) + if "json" in path.suffix: + return JsonConfig(data=data or "{}", path=path) + if "yaml" in path.suffix: + return YAMLConfig(data=data or "", path=path) + + # Should be unreachable. See the constant CONFIG_FILES. + raise ValueError( + f"Unsupported config file: {path.name} due to unknown file extension" + ) diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py index b6a07f4ced..860ca8ed5a 100644 --- a/commitizen/config/json_config.py +++ b/commitizen/config/json_config.py @@ -1,37 +1,43 @@ from __future__ import annotations import json -from pathlib import Path +from typing import TYPE_CHECKING from commitizen.exceptions import InvalidConfigurationError from commitizen.git import smart_open from .base_config import BaseConfig +if TYPE_CHECKING: + import sys + from pathlib import Path + + # Self is Python 3.11+ but backported in typing-extensions + if sys.version_info < (3, 11): + from typing_extensions import Self + else: + from typing import Self + class JsonConfig(BaseConfig): - def __init__(self, *, data: bytes | str, path: Path | str): + def __init__(self, *, data: bytes | str, path: Path) -> None: super().__init__() - self.is_empty_config = False - self.add_path(path) + self.path = path self._parse_setting(data) - def init_empty_config_content(self): - with smart_open(self.path, "a", encoding=self.encoding) as json_file: + def init_empty_config_content(self) -> None: + with smart_open( + self.path, "a", encoding=self._settings["encoding"] + ) as json_file: json.dump({"commitizen": {}}, json_file) - def set_key(self, key, value): - """Set or update a key in the conf. - - For now only strings are supported. - We use to update the version number. - """ + def set_key(self, key: str, value: object) -> Self: with open(self.path, "rb") as f: - parser = json.load(f) + config_doc = json.load(f) - parser["commitizen"][key] = value - with smart_open(self.path, "w", encoding=self.encoding) as f: - json.dump(parser, f, indent=2) + config_doc["commitizen"][key] = value + with smart_open(self.path, "w", encoding=self._settings["encoding"]) as f: + json.dump(config_doc, f, indent=2) return self def _parse_setting(self, data: bytes | str) -> None: diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py index 813389cbcf..b10cf9bd3e 100644 --- a/commitizen/config/toml_config.py +++ b/commitizen/config/toml_config.py @@ -1,47 +1,54 @@ from __future__ import annotations import os -from pathlib import Path +from typing import TYPE_CHECKING -from tomlkit import exceptions, parse, table +from tomlkit import TOMLDocument, exceptions, parse, table from commitizen.exceptions import InvalidConfigurationError from .base_config import BaseConfig +if TYPE_CHECKING: + import sys + from pathlib import Path + + # Self is Python 3.11+ but backported in typing-extensions + if sys.version_info < (3, 11): + from typing_extensions import Self + else: + from typing import Self + class TomlConfig(BaseConfig): - def __init__(self, *, data: bytes | str, path: Path | str): + def __init__(self, *, data: bytes | str, path: Path) -> None: super().__init__() - self.is_empty_config = False - self.add_path(path) + self.path = path self._parse_setting(data) - def init_empty_config_content(self): + def init_empty_config_content(self) -> None: + config_doc = TOMLDocument() if os.path.isfile(self.path): with open(self.path, "rb") as input_toml_file: - parser = parse(input_toml_file.read()) - else: - parser = parse("") + config_doc = parse(input_toml_file.read()) - with open(self.path, "wb") as output_toml_file: - if parser.get("tool") is None: - parser["tool"] = table() - parser["tool"]["commitizen"] = table() - output_toml_file.write(parser.as_string().encode(self.encoding)) + if config_doc.get("tool") is None: + config_doc["tool"] = table() + config_doc["tool"]["commitizen"] = table() # type: ignore[index] - def set_key(self, key, value): - """Set or update a key in the conf. + with open(self.path, "wb") as output_toml_file: + output_toml_file.write( + config_doc.as_string().encode(self._settings["encoding"]) + ) - For now only strings are supported. - We use to update the version number. - """ + def set_key(self, key: str, value: object) -> Self: with open(self.path, "rb") as f: - parser = parse(f.read()) + config_doc = parse(f.read()) - parser["tool"]["commitizen"][key] = value + config_doc["tool"]["commitizen"][key] = value # type: ignore[index] with open(self.path, "wb") as f: - f.write(parser.as_string().encode(self.encoding)) + f.write(config_doc.as_string().encode(self._settings["encoding"])) + return self def _parse_setting(self, data: bytes | str) -> None: @@ -58,6 +65,6 @@ def _parse_setting(self, data: bytes | str) -> None: raise InvalidConfigurationError(f"Failed to parse {self.path}: {e}") try: - self.settings.update(doc["tool"]["commitizen"]) # type: ignore + self.settings.update(doc["tool"]["commitizen"]) # type: ignore[index,typeddict-item] # TODO: fix this except exceptions.NonExistentKey: self.is_empty_config = True diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py index 2bb6fe3af8..58722d0f60 100644 --- a/commitizen/config/yaml_config.py +++ b/commitizen/config/yaml_config.py @@ -1,6 +1,6 @@ from __future__ import annotations -from pathlib import Path +from typing import TYPE_CHECKING import yaml @@ -9,16 +9,27 @@ from .base_config import BaseConfig +if TYPE_CHECKING: + import sys + from pathlib import Path + + # Self is Python 3.11+ but backported in typing-extensions + if sys.version_info < (3, 11): + from typing_extensions import Self + else: + from typing import Self + class YAMLConfig(BaseConfig): - def __init__(self, *, data: bytes | str, path: Path | str): + def __init__(self, *, data: bytes | str, path: Path) -> None: super().__init__() - self.is_empty_config = False - self.add_path(path) + self.path = path self._parse_setting(data) - def init_empty_config_content(self): - with smart_open(self.path, "a", encoding=self.encoding) as json_file: + def init_empty_config_content(self) -> None: + with smart_open( + self.path, "a", encoding=self._settings["encoding"] + ) as json_file: yaml.dump({"commitizen": {}}, json_file, explicit_start=True) def _parse_setting(self, data: bytes | str) -> None: @@ -41,17 +52,14 @@ def _parse_setting(self, data: bytes | str) -> None: except (KeyError, TypeError): self.is_empty_config = True - def set_key(self, key, value): - """Set or update a key in the conf. - - For now only strings are supported. - We use to update the version number. - """ + def set_key(self, key: str, value: object) -> Self: with open(self.path, "rb") as yaml_file: - parser = yaml.load(yaml_file, Loader=yaml.FullLoader) + config_doc = yaml.load(yaml_file, Loader=yaml.FullLoader) - parser["commitizen"][key] = value - with smart_open(self.path, "w", encoding=self.encoding) as yaml_file: - yaml.dump(parser, yaml_file, explicit_start=True) + config_doc["commitizen"][key] = value + with smart_open( + self.path, "w", encoding=self._settings["encoding"] + ) as yaml_file: + yaml.dump(config_doc, yaml_file, explicit_start=True) return self diff --git a/commitizen/cz/__init__.py b/commitizen/cz/__init__.py index cb17fe32cd..58753036ff 100644 --- a/commitizen/cz/__init__.py +++ b/commitizen/cz/__init__.py @@ -4,14 +4,18 @@ import pkgutil import sys import warnings -from collections.abc import Iterable if sys.version_info >= (3, 10): from importlib import metadata else: import importlib_metadata as metadata -from commitizen.cz.base import BaseCommitizen +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Iterable + + from commitizen.cz.base import BaseCommitizen def discover_plugins( diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py index 43455a74ca..e4bf88cc1c 100644 --- a/commitizen/cz/base.py +++ b/commitizen/cz/base.py @@ -1,15 +1,17 @@ from __future__ import annotations from abc import ABCMeta, abstractmethod -from collections.abc import Iterable -from typing import Any, Callable, Protocol +from typing import TYPE_CHECKING, Any, Callable, Protocol from jinja2 import BaseLoader, PackageLoader -from prompt_toolkit.styles import Style, merge_styles +from prompt_toolkit.styles import Style -from commitizen import git -from commitizen.config.base_config import BaseConfig -from commitizen.defaults import Questions +if TYPE_CHECKING: + from collections.abc import Iterable, Mapping + + from commitizen import git + from commitizen.config.base_config import BaseConfig + from commitizen.question import CzQuestion class MessageBuilderHook(Protocol): @@ -68,41 +70,34 @@ def __init__(self, config: BaseConfig) -> None: self.config.settings.update({"style": BaseCommitizen.default_style_config}) @abstractmethod - def questions(self) -> Questions: + def questions(self) -> list[CzQuestion]: """Questions regarding the commit message.""" @abstractmethod - def message(self, answers: dict) -> str: + def message(self, answers: Mapping[str, Any]) -> str: """Format your git message.""" @property - def style(self): - return merge_styles( + def style(self) -> Style: + return Style( [ - Style(BaseCommitizen.default_style_config), - Style(self.config.settings["style"]), + *BaseCommitizen.default_style_config, + *self.config.settings["style"], ] ) + @abstractmethod def example(self) -> str: """Example of the commit message.""" - raise NotImplementedError("Not Implemented yet") + @abstractmethod def schema(self) -> str: """Schema definition of the commit message.""" - raise NotImplementedError("Not Implemented yet") + @abstractmethod def schema_pattern(self) -> str: """Regex matching the schema used for message validation.""" - raise NotImplementedError("Not Implemented yet") + @abstractmethod def info(self) -> str: """Information about the standardized commit message.""" - raise NotImplementedError("Not Implemented yet") - - def process_commit(self, commit: str) -> str: - """Process commit for changelog. - - If not overwritten, it returns the first line of commit. - """ - return commit.split("\n")[0] diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index c7b88258cb..1d1930595b 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -1,47 +1,50 @@ +from __future__ import annotations + import os -import re +from typing import TYPE_CHECKING, TypedDict from commitizen import defaults from commitizen.cz.base import BaseCommitizen from commitizen.cz.utils import multiple_line_breaker, required_validator -from commitizen.defaults import Questions -__all__ = ["ConventionalCommitsCz"] +if TYPE_CHECKING: + from commitizen.question import CzQuestion +__all__ = ["ConventionalCommitsCz"] -def parse_scope(text): - if not text: - return "" - scope = text.strip().split() - if len(scope) == 1: - return scope[0] +def _parse_scope(text: str) -> str: + return "-".join(text.strip().split()) - return "-".join(scope) +def _parse_subject(text: str) -> str: + return required_validator(text.strip(".").strip(), msg="Subject is required.") -def parse_subject(text): - if isinstance(text, str): - text = text.strip(".").strip() - return required_validator(text, msg="Subject is required.") +class ConventionalCommitsAnswers(TypedDict): + prefix: str + scope: str + subject: str + body: str + footer: str + is_breaking_change: bool class ConventionalCommitsCz(BaseCommitizen): - 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 + 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.*)?" change_type_map = { "feat": "Feat", "fix": "Fix", "refactor": "Refactor", "perf": "Perf", } - changelog_pattern = defaults.bump_pattern + changelog_pattern = defaults.BUMP_PATTERN - def questions(self) -> Questions: - questions: Questions = [ + def questions(self) -> list[CzQuestion]: + return [ { "type": "list", "name": "prefix", @@ -113,12 +116,12 @@ def questions(self) -> Questions: "message": ( "What is the scope of this change? (class or file name): (press [enter] to skip)\n" ), - "filter": parse_scope, + "filter": _parse_scope, }, { "type": "input", "name": "subject", - "filter": parse_subject, + "filter": _parse_subject, "message": ( "Write a short and imperative summary of the code changes: (lower case and no period)\n" ), @@ -133,8 +136,8 @@ def questions(self) -> Questions: }, { "type": "confirm", - "message": "Is this a BREAKING CHANGE? Correlates with MAJOR in SemVer", "name": "is_breaking_change", + "message": "Is this a BREAKING CHANGE? Correlates with MAJOR in SemVer", "default": False, }, { @@ -146,9 +149,8 @@ def questions(self) -> Questions: ), }, ] - return questions - def message(self, answers: dict) -> str: + def message(self, answers: ConventionalCommitsAnswers) -> str: # type: ignore[override] prefix = answers["prefix"] scope = answers["scope"] subject = answers["subject"] @@ -156,18 +158,17 @@ def message(self, answers: dict) -> str: footer = answers["footer"] is_breaking_change = answers["is_breaking_change"] - if scope: - scope = f"({scope})" - if body: - body = f"\n\n{body}" + formatted_scope = f"({scope})" if scope else "" + title = f"{prefix}{formatted_scope}" if is_breaking_change: + if self.config.settings.get("breaking_change_exclamation_in_title", False): + title = f"{title}!" footer = f"BREAKING CHANGE: {footer}" - if footer: - footer = f"\n\n{footer}" - message = f"{prefix}{scope}: {subject}{body}{footer}" + formatted_body = f"\n\n{body}" if body else "" + formatted_footter = f"\n\n{footer}" if footer else "" - return message + return f"{title}: {subject}{formatted_body}{formatted_footter}" def example(self) -> str: return ( @@ -188,25 +189,32 @@ def schema(self) -> str: ) def schema_pattern(self) -> str: - PATTERN = ( + change_types = ( + "build", + "bump", + "chore", + "ci", + "docs", + "feat", + "fix", + "perf", + "refactor", + "revert", + "style", + "test", + ) + return ( r"(?s)" # To explicitly make . match new line - r"(build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert|bump)" # type - r"(\(\S+\))?!?:" # scope - r"( [^\n\r]+)" # subject + r"(" + "|".join(change_types) + r")" # type + r"(\(\S+\))?" # scope + r"!?" + r": " + r"([^\n\r]+)" # subject r"((\n\n.*)|(\s*))?$" ) - return PATTERN def info(self) -> str: dir_path = os.path.dirname(os.path.realpath(__file__)) filepath = os.path.join(dir_path, "conventional_commits_info.txt") with open(filepath, encoding=self.config.settings["encoding"]) as f: - content = f.read() - return content - - def process_commit(self, commit: str) -> str: - pat = re.compile(self.schema_pattern()) - m = re.match(pat, commit) - if m is None: - return "" - return m.group(3).strip() + return f.read() diff --git a/commitizen/cz/customize/__init__.py b/commitizen/cz/customize/__init__.py index c5af001a79..0aedb9337a 100644 --- a/commitizen/cz/customize/__init__.py +++ b/commitizen/cz/customize/__init__.py @@ -1 +1 @@ -from .customize import CustomizeCommitsCz # noqa +from .customize import CustomizeCommitsCz # noqa: F401 diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py index d53ae29f1b..0bc31db30a 100644 --- a/commitizen/cz/customize/customize.py +++ b/commitizen/cz/customize/customize.py @@ -1,9 +1,14 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any if TYPE_CHECKING: + from collections.abc import Mapping + from jinja2 import Template + + from commitizen.config import BaseConfig + from commitizen.question import CzQuestion else: try: from jinja2 import Template @@ -12,66 +17,45 @@ from commitizen import defaults -from commitizen.config import BaseConfig from commitizen.cz.base import BaseCommitizen -from commitizen.defaults import Questions from commitizen.exceptions import MissingCzCustomizeConfigError __all__ = ["CustomizeCommitsCz"] 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): + def __init__(self, config: BaseConfig) -> None: super().__init__(config) if "customize" not in self.config.settings: 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 - - def questions(self) -> Questions: - return self.custom_settings.get("questions", [{}]) - - def message(self, answers: dict) -> str: + 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) -> list[CzQuestion]: + return self.custom_settings.get("questions", [{}]) # type: ignore[return-value] + + def message(self, answers: Mapping[str, Any]) -> 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.substitute(**answers) # type: ignore[attr-defined,no-any-return] # pragma: no cover # TODO: check if we can fix this + return message_template.render(**answers) def example(self) -> str: return self.custom_settings.get("example") or "" @@ -83,12 +67,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/cz/jira/jira.py b/commitizen/cz/jira/jira.py index b8fd056a71..bbe4fc5ee3 100644 --- a/commitizen/cz/jira/jira.py +++ b/commitizen/cz/jira/jira.py @@ -1,14 +1,21 @@ +from __future__ import annotations + import os +from typing import TYPE_CHECKING from commitizen.cz.base import BaseCommitizen -from commitizen.defaults import Questions + +if TYPE_CHECKING: + from collections.abc import Mapping + + from commitizen.question import CzQuestion __all__ = ["JiraSmartCz"] class JiraSmartCz(BaseCommitizen): - def questions(self) -> Questions: - questions = [ + def questions(self) -> list[CzQuestion]: + return [ { "type": "input", "name": "message", @@ -42,20 +49,12 @@ def questions(self) -> Questions: "filter": lambda x: "#comment " + x if x else "", }, ] - return questions - def message(self, answers: dict) -> str: + def message(self, answers: Mapping[str, str]) -> str: return " ".join( - filter( - bool, - [ - answers["message"], - answers["issues"], - answers["workflow"], - answers["time"], - answers["comment"], - ], - ) + x + for k in ("message", "issues", "workflow", "time", "comment") + if (x := answers.get(k)) ) def example(self) -> str: @@ -68,7 +67,7 @@ def example(self) -> str: ) def schema(self) -> str: - return " # " # noqa + return " # " def schema_pattern(self) -> str: return r".*[A-Z]{2,}\-[0-9]+( #| .* #).+( #.+)*" @@ -77,5 +76,4 @@ def info(self) -> str: dir_path = os.path.dirname(os.path.realpath(__file__)) filepath = os.path.join(dir_path, "jira_info.txt") with open(filepath, encoding=self.config.settings["encoding"]) as f: - content = f.read() - return content + return f.read() diff --git a/commitizen/cz/utils.py b/commitizen/cz/utils.py index 7bc89673c6..ba5eace44a 100644 --- a/commitizen/cz/utils.py +++ b/commitizen/cz/utils.py @@ -1,32 +1,31 @@ import os import re import tempfile +from pathlib import Path from commitizen import git from commitizen.cz import exceptions +_RE_LOCAL_VERSION = re.compile(r"\+.+") -def required_validator(answer, msg=None): + +def required_validator(answer: str, msg: object = None) -> str: if not answer: raise exceptions.AnswerRequiredError(msg) return answer -def multiple_line_breaker(answer, sep="|"): +def multiple_line_breaker(answer: str, sep: str = "|") -> str: return "\n".join(line.strip() for line in answer.split(sep) if line) def strip_local_version(version: str) -> str: - return re.sub(r"\+.+", "", version) + return _RE_LOCAL_VERSION.sub("", version) -def get_backup_file_path() -> str: +def get_backup_file_path() -> Path: project_root = git.find_git_project_root() - - if project_root is None: - project = "" - else: - project = project_root.as_posix().replace("/", "%") + project = project_root.as_posix().replace("/", "%") if project_root else "" user = os.environ.get("USER", "") - return os.path.join(tempfile.gettempdir(), f"cz.commit%{user}%{project}.backup") + return Path(tempfile.gettempdir(), f"cz.commit%{user}%{project}.backup") diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 0b78e1b0bb..b91fe4879c 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -1,12 +1,14 @@ from __future__ import annotations -import pathlib +import warnings from collections import OrderedDict from collections.abc import Iterable, MutableMapping, Sequence -from typing import Any, TypedDict +from typing import TYPE_CHECKING, Any, TypedDict -# Type -Questions = Iterable[MutableMapping[str, Any]] +if TYPE_CHECKING: + import pathlib + + from commitizen.question import CzQuestion class CzSettings(TypedDict, total=False): @@ -15,7 +17,7 @@ class CzSettings(TypedDict, total=False): bump_map_major_version_zero: OrderedDict[str, str] change_type_order: list[str] - questions: Questions + questions: Iterable[CzQuestion] example: str | None schema_pattern: str | None schema: str | None @@ -28,40 +30,44 @@ class CzSettings(TypedDict, total=False): class Settings(TypedDict, total=False): - name: str - version: str | None - version_files: list[str] - version_provider: str | None - version_scheme: str | None - version_type: str | None - tag_format: str - legacy_tag_formats: Sequence[str] - ignored_tag_formats: Sequence[str] - bump_message: str | None - retry_after_failure: bool allow_abort: bool allowed_prefixes: list[str] + always_signoff: bool + annotated_tag: bool + bump_message: str | None + change_type_map: dict[str, str] changelog_file: str changelog_format: str | None changelog_incremental: bool - changelog_start_rev: str | None changelog_merge_prerelease: bool - update_changelog_on_bump: bool - use_shortcuts: bool - style: list[tuple[str, str]] + changelog_start_rev: str | None customize: CzSettings + encoding: str + extras: dict[str, Any] + gpg_sign: bool + ignored_tag_formats: Sequence[str] + legacy_tag_formats: Sequence[str] major_version_zero: bool - pre_bump_hooks: list[str] | None + message_length_limit: int | None + name: str post_bump_hooks: list[str] | None + pre_bump_hooks: list[str] | None prerelease_offset: int - encoding: str - always_signoff: bool + retry_after_failure: bool + style: list[tuple[str, str]] + tag_format: str template: str | None - extras: dict[str, Any] + update_changelog_on_bump: bool + use_shortcuts: bool + version_files: list[str] + version_provider: str | None + version_scheme: str | None + version_type: str | None + version: str | None + breaking_change_exclamation_in_title: bool -name: str = "cz_conventional_commits" -config_files: list[str] = [ +CONFIG_FILES: list[str] = [ "pyproject.toml", ".cz.toml", ".cz.json", @@ -70,10 +76,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", @@ -90,6 +96,7 @@ class Settings(TypedDict, total=False): "Pull request", "fixup!", "squash!", + "amend!", ], "changelog_file": "CHANGELOG.md", "changelog_format": None, # default guessed from changelog_file @@ -102,10 +109,12 @@ 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": {}, + "breaking_change_exclamation_in_title": False, + "message_length_limit": None, # None for no limit } MAJOR = "MAJOR" @@ -114,8 +123,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 +134,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,14 +144,14 @@ 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( version_regex: str, ) -> dict[str, str]: - regexs = { + regexes = { "version": version_regex, "major": r"(?P\d+)", "minor": r"(?P\d+)", @@ -151,6 +160,39 @@ def get_tag_regexes( "devrelease": r"(?P\.dev\d+)?", } return { - **{f"${k}": v for k, v in regexs.items()}, - **{f"${{{k}}}": v for k, v in regexs.items()}, + **{f"${k}": v for k, v in regexes.items()}, + **{f"${{{k}}}": v for k, v in regexes.items()}, + } + + +# Type +Questions = Iterable[MutableMapping[str, Any]] # TODO: remove this in v5 + + +def __getattr__(name: str) -> Any: + # PEP-562: deprecate module-level variable + + # {"deprecated key": (value, "new key")} + deprecated_vars = { + "bump_pattern": (BUMP_PATTERN, "BUMP_PATTERN"), + "bump_map": (BUMP_MAP, "BUMP_MAP"), + "bump_map_major_version_zero": ( + BUMP_MAP_MAJOR_VERSION_ZERO, + "BUMP_MAP_MAJOR_VERSION_ZERO", + ), + "bump_message": (BUMP_MESSAGE, "BUMP_MESSAGE"), + "change_type_order": (CHANGE_TYPE_ORDER, "CHANGE_TYPE_ORDER"), + "encoding": (ENCODING, "ENCODING"), + "name": (DEFAULT_SETTINGS["name"], "DEFAULT_SETTINGS['name']"), + "Questions": (Questions, "Iterable[CzQuestion]"), } + if name in deprecated_vars: + value, replacement = deprecated_vars[name] + warnings.warn( + f"{name} is deprecated and will be removed in v5. " + f"Use {replacement} instead.", + DeprecationWarning, + stacklevel=2, + ) + return value + raise AttributeError(f"{name} is not an attribute of {__name__}") diff --git a/commitizen/exceptions.py b/commitizen/exceptions.py index 29733b624b..52193e6ccc 100644 --- a/commitizen/exceptions.py +++ b/commitizen/exceptions.py @@ -1,9 +1,12 @@ -import enum +from __future__ import annotations + +from enum import IntEnum +from typing import Any from commitizen import out -class ExitCode(enum.IntEnum): +class ExitCode(IntEnum): EXPECTED_EXIT = 0 NO_COMMITIZEN_FOUND = 1 NOT_A_GIT_PROJECT = 2 @@ -26,7 +29,7 @@ class ExitCode(enum.IntEnum): INVALID_CONFIGURATION = 19 NOT_ALLOWED = 20 NO_INCREMENT = 21 - UNRECOGNIZED_CHARACTERSET_ENCODING = 22 + CHARACTER_SET_DECODE_ERROR = 22 GIT_COMMAND_ERROR = 23 INVALID_MANUAL_VERSION = 24 INIT_FAILED = 25 @@ -38,9 +41,15 @@ class ExitCode(enum.IntEnum): CONFIG_FILE_IS_EMPTY = 31 COMMIT_MESSAGE_LENGTH_LIMIT_EXCEEDED = 32 + @classmethod + def from_str(cls, value: str) -> ExitCode: + if value.isdecimal(): + return cls(int(value)) + return cls[value.strip()] + class CommitizenException(Exception): - def __init__(self, *args, **kwargs): + def __init__(self, *args: str, **kwargs: Any) -> None: self.output_method = kwargs.get("output_method") or out.error self.exit_code: ExitCode = self.__class__.exit_code if args: @@ -50,50 +59,57 @@ def __init__(self, *args, **kwargs): else: self.message = "" - def __str__(self): + def __str__(self) -> str: return self.message class ExpectedExit(CommitizenException): exit_code = ExitCode.EXPECTED_EXIT - def __init__(self, *args, **kwargs): - output_method = kwargs.get("output_method") or out.write - kwargs["output_method"] = output_method + def __init__(self, *args: str, **kwargs: Any) -> None: + kwargs["output_method"] = kwargs.get("output_method") or out.write super().__init__(*args, **kwargs) class DryRunExit(ExpectedExit): - pass - - -class GetNextExit(ExpectedExit): - pass + """Exit due to passing `--dry-run` option""" class NoneIncrementExit(CommitizenException): + """The commits found are not eligible to be bumped""" + exit_code = ExitCode.NO_INCREMENT class NoCommitizenFoundException(CommitizenException): + """Using a cz (e.g., `cz_jira`) that cannot be found in your system""" + exit_code = ExitCode.NO_COMMITIZEN_FOUND class NotAGitProjectError(CommitizenException): + """Not in a git project""" + exit_code = ExitCode.NOT_A_GIT_PROJECT message = "fatal: not a git repository (or any of the parent directories): .git" class MissingCzCustomizeConfigError(CommitizenException): + """Configuration is missing for `cz_customize`""" + exit_code = ExitCode.MISSING_CZ_CUSTOMIZE_CONFIG message = "fatal: customize is not set in configuration file." class NoCommitsFoundError(CommitizenException): + """No commits found""" + exit_code = ExitCode.NO_COMMITS_FOUND class NoVersionSpecifiedError(CommitizenException): + """Version is not specified in configuration file""" + exit_code = ExitCode.NO_VERSION_SPECIFIED message = ( "[NO_VERSION_SPECIFIED]\n" @@ -103,111 +119,166 @@ class NoVersionSpecifiedError(CommitizenException): class NoPatternMapError(CommitizenException): + """bump / changelog pattern or map can not be found in configuration file""" + exit_code = ExitCode.NO_PATTERN_MAP class BumpCommitFailedError(CommitizenException): + """Commit failed when bumping version""" + exit_code = ExitCode.BUMP_COMMIT_FAILED class BumpTagFailedError(CommitizenException): + """Tag failed when bumping version""" + exit_code = ExitCode.BUMP_TAG_FAILED class CurrentVersionNotFoundError(CommitizenException): + """Current version cannot be found in `version_files`""" + exit_code = ExitCode.CURRENT_VERSION_NOT_FOUND class NoAnswersError(CommitizenException): + """No user response given""" + exit_code = ExitCode.NO_ANSWERS class CommitError(CommitizenException): + """git commit error""" + exit_code = ExitCode.COMMIT_ERROR class NoCommitBackupError(CommitizenException): + """Commit backup file is not found""" + exit_code = ExitCode.NO_COMMIT_BACKUP message = "No commit backup found" class NothingToCommitError(CommitizenException): + """Nothing in staging to be committed""" + exit_code = ExitCode.NOTHING_TO_COMMIT class CustomError(CommitizenException): + """`CzException` raised""" + exit_code = ExitCode.CUSTOM_ERROR class InvalidCommitMessageError(CommitizenException): + """The commit message does not pass `cz check`""" + exit_code = ExitCode.INVALID_COMMIT_MSG class NoRevisionError(CommitizenException): + """No revision found""" + exit_code = ExitCode.NO_REVISION message = "No tag found to do an incremental changelog" class NoCommandFoundError(CommitizenException): + """No command found when running Commitizen cli (e.g., `cz --debug`)""" + exit_code = ExitCode.NO_COMMAND_FOUND message = "Command is required" class InvalidCommandArgumentError(CommitizenException): + """The argument provided to the command is invalid (e.g. `cz check -commit-msg-file filename --rev-range master..`)""" + exit_code = ExitCode.INVALID_COMMAND_ARGUMENT class InvalidConfigurationError(CommitizenException): + """An error was found in the Commitizen Configuration, such as duplicates in `change_type_order`""" + exit_code = ExitCode.INVALID_CONFIGURATION class NotAllowed(CommitizenException): + """`--incremental` cannot be combined with a `rev_range`""" + exit_code = ExitCode.NOT_ALLOWED class CharacterSetDecodeError(CommitizenException): - exit_code = ExitCode.UNRECOGNIZED_CHARACTERSET_ENCODING + """The character encoding of the command output could not be determined""" + + exit_code = ExitCode.CHARACTER_SET_DECODE_ERROR class GitCommandError(CommitizenException): + """Unexpected failure while calling a git command""" + exit_code = ExitCode.GIT_COMMAND_ERROR class InvalidManualVersion(CommitizenException): + """Manually provided version is invalid""" + exit_code = ExitCode.INVALID_MANUAL_VERSION class InitFailedError(CommitizenException): + """Failed to initialize pre-commit""" + exit_code = ExitCode.INIT_FAILED class RunHookError(CommitizenException): + """An error occurred during a hook execution""" + exit_code = ExitCode.RUN_HOOK_FAILED class VersionProviderUnknown(CommitizenException): + """Unknown `version_provider`""" + exit_code = ExitCode.VERSION_PROVIDER_UNKNOWN class VersionSchemeUnknown(CommitizenException): + """Unknown `version_scheme`""" + exit_code = ExitCode.VERSION_SCHEME_UNKNOWN class ChangelogFormatUnknown(CommitizenException): + """Unknown `changelog_format` or cannot be determined by the file extension""" + exit_code = ExitCode.CHANGELOG_FORMAT_UNKNOWN message = "Unknown changelog format identifier" class ConfigFileNotFound(CommitizenException): + """The configuration file is not found""" + exit_code = ExitCode.CONFIG_FILE_NOT_FOUND message = "Cannot found the config file, please check your file path again." class ConfigFileIsEmpty(CommitizenException): + """The configuration file is empty""" + exit_code = ExitCode.CONFIG_FILE_IS_EMPTY message = "Config file is empty, please check your file path again." class CommitMessageLengthExceededError(CommitizenException): + """The commit message length exceeds the given limit.""" + exit_code = ExitCode.COMMIT_MESSAGE_LENGTH_LIMIT_EXCEEDED message = "Length of commit message exceeds the given limit." + + +# When adding / updating a new exit code, please update the documentation of the exit codes in docs/exit_codes.md diff --git a/commitizen/factory.py b/commitizen/factory.py index 09af5fd0f7..d9e99fb771 100644 --- a/commitizen/factory.py +++ b/commitizen/factory.py @@ -4,16 +4,14 @@ 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: - _cz = registry[name](config) + return registry[name](config) except KeyError: msg_error = ( "The committer has not been found in the system.\n\n" f"Try running 'pip install {name}'\n" ) raise NoCommitizenFoundException(msg_error) - else: - return _cz diff --git a/commitizen/git.py b/commitizen/git.py index 19ca46b6c3..e598ff065c 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -2,56 +2,67 @@ import os from enum import Enum -from os import linesep +from functools import lru_cache from pathlib import Path from tempfile import NamedTemporaryFile from commitizen import cmd, out from commitizen.exceptions import GitCommandError -UNIX_EOL = "\n" -WINDOWS_EOL = "\r\n" - -class EOLTypes(Enum): +class EOLType(Enum): """The EOL type from `git config core.eol`.""" LF = "lf" CRLF = "crlf" NATIVE = "native" - def get_eol_for_open(self) -> str: + @classmethod + def for_open(cls) -> str: + c = cmd.run("git config core.eol") + eol = c.out.strip().upper() + return cls._char_for_open()[cls._safe_cast(eol)] + + @classmethod + def _safe_cast(cls, eol: str) -> EOLType: + try: + return cls[eol] + except KeyError: + return cls.NATIVE + + @classmethod + @lru_cache + def _char_for_open(cls) -> dict[EOLType, str]: """Get the EOL character for `open()`.""" - map = { - EOLTypes.CRLF: WINDOWS_EOL, - EOLTypes.LF: UNIX_EOL, - EOLTypes.NATIVE: linesep, + return { + cls.LF: "\n", + cls.CRLF: "\r\n", + cls.NATIVE: os.linesep, } - return map[self] - class GitObject: rev: str name: str date: str - def __eq__(self, other) -> bool: - if not hasattr(other, "rev"): - return False - return self.rev == other.rev # type: ignore + def __eq__(self, other: object) -> bool: + return hasattr(other, "rev") and self.rev == other.rev + + def __hash__(self) -> int: + return hash(self.rev) class GitCommit(GitObject): def __init__( self, - rev, - title, + rev: str, + title: str, body: str = "", author: str = "", author_email: str = "", parents: list[str] | None = None, - ): + ) -> None: self.rev = rev.strip() self.title = title.strip() self.body = body.strip() @@ -60,26 +71,86 @@ def __init__( self.parents = parents or [] @property - def message(self): + def message(self) -> str: return f"{self.title}\n\n{self.body}".strip() - def __repr__(self): + @classmethod + def from_rev_and_commit(cls, rev_and_commit: str) -> GitCommit: + """Create a GitCommit instance from a formatted commit string. + + This method parses a multi-line string containing commit information in the following format: + ``` + + + + <author> + <author_email> + <body_line_1> + <body_line_2> + ... + ``` + + Args: + rev_and_commit (str): A string containing commit information with fields separated by newlines. + - rev: The commit hash/revision + - parents: Space-separated list of parent commit hashes + - title: The commit title/message + - author: The commit author's name + - author_email: The commit author's email + - body: Optional multi-line commit body + + Returns: + GitCommit: A new GitCommit instance with the parsed information. + + Example: + >>> commit_str = '''abc123 + ... def456 ghi789 + ... feat: add new feature + ... John Doe + ... john@example.com + ... This is a detailed description + ... of the new feature''' + >>> commit = GitCommit.from_rev_and_commit(commit_str) + >>> commit.rev + 'abc123' + >>> commit.title + 'feat: add new feature' + >>> commit.parents + ['def456', 'ghi789'] + """ + rev, parents, title, author, author_email, *body_list = rev_and_commit.split( + "\n" + ) + return cls( + rev=rev.strip(), + title=title.strip(), + body="\n".join(body_list).strip(), + author=author, + author_email=author_email, + parents=[p for p in parents.strip().split(" ") if p], + ) + + def __repr__(self) -> str: return f"{self.title} ({self.rev})" class GitTag(GitObject): - def __init__(self, name, rev, date): + def __init__(self, name: str, rev: str, date: str) -> None: self.rev = rev.strip() self.name = name.strip() self._date = date.strip() - def __repr__(self): + def __repr__(self) -> str: return f"GitTag('{self.name}', '{self.rev}', '{self.date}')" @property - def date(self): + def date(self) -> str: return self._date + @date.setter + def date(self, value: str) -> None: + self._date = value + @classmethod def from_line(cls, line: str, inner_delimiter: str) -> GitTag: name, objectname, date, obj = line.split(inner_delimiter) @@ -92,22 +163,18 @@ def from_line(cls, line: str, inner_delimiter: str) -> GitTag: def tag( tag: str, annotated: bool = False, signed: bool = False, msg: str | None = None ) -> cmd.Command: - _opt = "" - if annotated: - _opt = f"-a {tag} -m" - if signed: - _opt = f"-s {tag} -m" + if not annotated and not signed: + return cmd.run(f"git tag {tag}") # according to https://git-scm.com/book/en/v2/Git-Basics-Tagging, # we're not able to create lightweight tag with message. # by adding message, we make it a annotated tags - c = cmd.run(f'git tag {_opt} "{tag if _opt == "" or msg is None else msg}"') - return c + option = "-s" if signed else "-a" # The else case is for annotated tags + return cmd.run(f'git tag {option} {tag} -m "{msg or tag}"') def add(*args: str) -> cmd.Command: - c = cmd.run(f"git add {' '.join(args)}") - return c + return cmd.run(f"git add {' '.join(args)}") def commit( @@ -119,48 +186,40 @@ def commit( f.write(message.encode("utf-8")) f.close() - command = f'git commit {args} -F "{f.name}"' - - if committer_date and os.name == "nt": # pragma: no cover - # Using `cmd /v /c "{command}"` sets environment variables only for that command - command = f'cmd /v /c "set GIT_COMMITTER_DATE={committer_date}&& {command}"' - elif committer_date: - command = f"GIT_COMMITTER_DATE={committer_date} {command}" - + command = _create_commit_cmd_string(args, committer_date, f.name) c = cmd.run(command) os.unlink(f.name) return c +def _create_commit_cmd_string(args: str, committer_date: str | None, name: str) -> str: + command = f'git commit {args} -F "{name}"' + if not committer_date: + return command + if os.name != "nt": + return f"GIT_COMMITTER_DATE={committer_date} {command}" + # Using `cmd /v /c "{command}"` sets environment variables only for that command + return f'cmd /v /c "set GIT_COMMITTER_DATE={committer_date}&& {command}"' + + def get_commits( start: str | None = None, - end: str = "HEAD", + end: str | None = None, *, args: str = "", ) -> list[GitCommit]: """Get the commits between start and end.""" + if end is None: + end = "HEAD" git_log_entries = _get_log_as_str_list(start, end, args) - git_commits = [] - for rev_and_commit in git_log_entries: - if not rev_and_commit: - continue - rev, parents, title, author, author_email, *body_list = rev_and_commit.split( - "\n" - ) - if rev_and_commit: - git_commit = GitCommit( - rev=rev.strip(), - title=title.strip(), - body="\n".join(body_list).strip(), - author=author, - author_email=author_email, - parents=[p for p in parents.strip().split(" ") if p], - ) - git_commits.append(git_commit) - return git_commits - - -def get_filenames_in_commit(git_reference: str = ""): + return [ + GitCommit.from_rev_and_commit(rev_and_commit) + for rev_and_commit in git_log_entries + if rev_and_commit + ] + + +def get_filenames_in_commit(git_reference: str = "") -> list[str]: """Get the list of files that were committed in the requested git reference. :param git_reference: a git reference as accepted by `git show`, default: the last commit @@ -170,8 +229,7 @@ def get_filenames_in_commit(git_reference: str = ""): c = cmd.run(f"git show --name-only --pretty=format: {git_reference}") if c.return_code == 0: return c.out.strip().split("\n") - else: - raise GitCommandError(c.err) + raise GitCommandError(c.err) def get_tags( @@ -179,7 +237,7 @@ def get_tags( ) -> list[GitTag]: inner_delimiter = "---inner_delimiter---" formatter = ( - f'"%(refname:lstrip=2){inner_delimiter}' + f'"%(refname:strip=2){inner_delimiter}' f"%(objectname){inner_delimiter}" f"%(creatordate:format:{dateformat}){inner_delimiter}" f'%(object)"' @@ -197,16 +255,11 @@ def get_tags( if c.err: out.warn(f"Attempting to proceed after: {c.err}") - if not c.out: - return [] - - git_tags = [ + return [ GitTag.from_line(line=line, inner_delimiter=inner_delimiter) for line in c.out.split("\n")[:-1] ] - return git_tags - def tag_exist(tag: str) -> bool: c = cmd.run(f"git tag --list {tag}") @@ -231,18 +284,18 @@ def get_tag_message(tag: str) -> str | None: return c.out.strip() -def get_tag_names() -> list[str | None]: +def get_tag_names() -> list[str]: c = cmd.run("git tag --list") if c.err: return [] - return [tag.strip() for tag in c.out.split("\n") if tag.strip()] + return [tag for raw in c.out.split("\n") if (tag := raw.strip())] def find_git_project_root() -> Path | None: c = cmd.run("git rev-parse --show-toplevel") - if not c.err: - return Path(c.out.strip()) - return None + if c.err: + return None + return Path(c.out.strip()) def is_staging_clean() -> bool: @@ -253,32 +306,7 @@ def is_staging_clean() -> bool: def is_git_project() -> bool: c = cmd.run("git rev-parse --is-inside-work-tree") - if c.out.strip() == "true": - return True - return False - - -def get_eol_style() -> EOLTypes: - c = cmd.run("git config core.eol") - eol = c.out.strip().lower() - - # We enumerate the EOL types of the response of - # `git config core.eol`, and map it to our enumration EOLTypes. - # - # It is just like the variant of the "match" syntax. - map = { - "lf": EOLTypes.LF, - "crlf": EOLTypes.CRLF, - "native": EOLTypes.NATIVE, - } - - # If the response of `git config core.eol` is in the map: - if eol in map: - return map[eol] - else: - # The default value is "native". - # https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreeol - return map["native"] + return c.out.strip() == "true" def get_core_editor() -> str | None: @@ -288,25 +316,26 @@ def get_core_editor() -> str | None: return None -def smart_open(*args, **kargs): +def smart_open(*args, **kwargs): # type: ignore[no-untyped-def,unused-ignore] # noqa: ANN201 """Open a file with the EOL style determined from Git.""" - return open(*args, newline=get_eol_style().get_eol_for_open(), **kargs) + return open(*args, newline=EOLType.for_open(), **kwargs) def _get_log_as_str_list(start: str | None, end: str, args: str) -> list[str]: """Get string representation of each log entry""" delimiter = "----------commit-delimiter----------" log_format: str = "%H%n%P%n%s%n%an%n%ae%n%b" - git_log_cmd = ( - f"git -c log.showSignature=False log --pretty={log_format}{delimiter} {args}" - ) - if start: - command = f"{git_log_cmd} {start}..{end}" - else: - command = f"{git_log_cmd} {end}" + command_range = f"{start}..{end}" if start else end + command = f"git -c log.showSignature=False log --pretty={log_format}{delimiter} {args} {command_range}" + c = cmd.run(command) if c.return_code != 0: raise GitCommandError(c.err) - if not c.out: - return [] return c.out.split(f"{delimiter}\n") + + +def get_default_branch() -> str: + c = cmd.run("git symbolic-ref refs/remotes/origin/HEAD") + if c.return_code != 0: + raise GitCommandError(c.err) + return c.out.strip() diff --git a/commitizen/hooks.py b/commitizen/hooks.py index f5505d0e82..10560d5eae 100644 --- a/commitizen/hooks.py +++ b/commitizen/hooks.py @@ -1,12 +1,16 @@ from __future__ import annotations import os +from typing import TYPE_CHECKING from commitizen import cmd, out from commitizen.exceptions import RunHookError +if TYPE_CHECKING: + from collections.abc import Mapping -def run(hooks, _env_prefix="CZ_", **env): + +def run(hooks: str | list[str], _env_prefix: str = "CZ_", **env: object) -> None: if isinstance(hooks, str): hooks = [hooks] @@ -24,7 +28,7 @@ def run(hooks, _env_prefix="CZ_", **env): raise RunHookError(f"Running hook '{hook}' failed") -def _format_env(prefix: str, env: dict[str, str]) -> dict[str, str]: +def _format_env(prefix: str, env: Mapping[str, object]) -> dict[str, str]: """_format_env() prefixes all given environment variables with the given prefix so it can be passed directly to cmd.run().""" penv = dict(os.environ) diff --git a/commitizen/out.py b/commitizen/out.py index 40342e9de5..1bbfe4329d 100644 --- a/commitizen/out.py +++ b/commitizen/out.py @@ -1,5 +1,6 @@ import io import sys +from typing import Any from termcolor import colored @@ -8,12 +9,12 @@ sys.stdout.reconfigure(encoding="utf-8") -def write(value: str, *args) -> None: +def write(value: str, *args: object) -> None: """Intended to be used when value is multiline.""" print(value, *args) -def line(value: str, *args, **kwargs) -> None: +def line(value: str, *args: object, **kwargs: Any) -> None: """Wrapper in case I want to do something different later.""" print(value, *args, **kwargs) @@ -33,7 +34,7 @@ def info(value: str) -> None: line(message) -def diagnostic(value: str): +def diagnostic(value: str) -> None: line(value, file=sys.stderr) diff --git a/commitizen/project_info.py b/commitizen/project_info.py new file mode 100644 index 0000000000..a754388008 --- /dev/null +++ b/commitizen/project_info.py @@ -0,0 +1,47 @@ +"""Resolves project information about the current working directory.""" + +import shutil +from pathlib import Path +from typing import Literal + + +def is_pre_commit_installed() -> bool: + return bool(shutil.which("pre-commit")) + + +def get_default_version_provider() -> Literal[ + "commitizen", "cargo", "composer", "npm", "pep621", "poetry", "uv" +]: + pyproject_path = Path("pyproject.toml") + if pyproject_path.is_file(): + if "[tool.poetry]" in pyproject_path.read_text(): + return "poetry" + if Path("uv.lock").is_file(): + return "uv" + return "pep621" + + if Path("setup.py").is_file(): + return "pep621" + + if Path("Cargo.toml").is_file(): + return "cargo" + + if Path("package.json").is_file(): + return "npm" + + if Path("composer.json").is_file(): + return "composer" + + return "commitizen" + + +def get_default_config_filename() -> Literal["pyproject.toml", ".cz.toml"]: + return "pyproject.toml" if Path("pyproject.toml").is_file() else ".cz.toml" + + +def get_default_version_scheme() -> Literal["pep440", "semver"]: + return ( + "pep440" + if Path("pyproject.toml").is_file() or Path("setup.py").is_file() + else "semver" + ) diff --git a/commitizen/providers/__init__.py b/commitizen/providers/__init__.py index 9cf4ce5927..4cef578ab9 100644 --- a/commitizen/providers/__init__.py +++ b/commitizen/providers/__init__.py @@ -1,16 +1,14 @@ from __future__ import annotations import sys -from typing import cast +from typing import TYPE_CHECKING, cast if sys.version_info >= (3, 10): from importlib import metadata else: import importlib_metadata as metadata -from commitizen.config.base_config import BaseConfig from commitizen.exceptions import VersionProviderUnknown -from commitizen.providers.base_provider import VersionProvider from commitizen.providers.cargo_provider import CargoProvider from commitizen.providers.commitizen_provider import CommitizenProvider from commitizen.providers.composer_provider import ComposerProvider @@ -20,8 +18,11 @@ from commitizen.providers.scm_provider import ScmProvider from commitizen.providers.uv_provider import UvProvider +if TYPE_CHECKING: + from commitizen.config.base_config import BaseConfig + from commitizen.providers.base_provider import VersionProvider + __all__ = [ - "get_provider", "CargoProvider", "CommitizenProvider", "ComposerProvider", @@ -30,6 +31,7 @@ "PoetryProvider", "ScmProvider", "UvProvider", + "get_provider", ] PROVIDER_ENTRYPOINT = "commitizen.provider" @@ -48,4 +50,4 @@ def get_provider(config: BaseConfig) -> VersionProvider: except ValueError: raise VersionProviderUnknown(f'Version Provider "{provider_name}" unknown.') provider_cls = ep.load() - return cast(VersionProvider, provider_cls(config)) + return cast("VersionProvider", provider_cls(config)) diff --git a/commitizen/providers/base_provider.py b/commitizen/providers/base_provider.py index 34048318e2..84b745e326 100644 --- a/commitizen/providers/base_provider.py +++ b/commitizen/providers/base_provider.py @@ -3,11 +3,14 @@ import json from abc import ABC, abstractmethod from pathlib import Path -from typing import Any, ClassVar +from typing import TYPE_CHECKING, Any, ClassVar import tomlkit -from commitizen.config.base_config import BaseConfig +if TYPE_CHECKING: + from collections.abc import Mapping + + from commitizen.config.base_config import BaseConfig class VersionProvider(ABC): @@ -19,7 +22,7 @@ class VersionProvider(ABC): config: BaseConfig - def __init__(self, config: BaseConfig): + def __init__(self, config: BaseConfig) -> None: self.config = config @abstractmethod @@ -29,7 +32,7 @@ def get_version(self) -> str: """ @abstractmethod - def set_version(self, version: str): + def set_version(self, version: str) -> None: """ Set the new current version """ @@ -58,15 +61,15 @@ def get_version(self) -> str: document = json.loads(self.file.read_text()) return self.get(document) - def set_version(self, version: str): + def set_version(self, version: str) -> None: document = json.loads(self.file.read_text()) self.set(document, version) self.file.write_text(json.dumps(document, indent=self.indent) + "\n") - def get(self, document: dict[str, Any]) -> str: - return document["version"] # type: ignore + def get(self, document: Mapping[str, str]) -> str: + return document["version"] - def set(self, document: dict[str, Any], version: str): + def set(self, document: dict[str, Any], version: str) -> None: document["version"] = version @@ -79,13 +82,13 @@ def get_version(self) -> str: document = tomlkit.parse(self.file.read_text()) return self.get(document) - def set_version(self, version: str): + def set_version(self, version: str) -> None: document = tomlkit.parse(self.file.read_text()) self.set(document, version) self.file.write_text(tomlkit.dumps(document)) def get(self, document: tomlkit.TOMLDocument) -> str: - return document["project"]["version"] # type: ignore + return document["project"]["version"] # type: ignore[index,return-value] - def set(self, document: tomlkit.TOMLDocument, version: str): - document["project"]["version"] = version # type: ignore + def set(self, document: tomlkit.TOMLDocument, version: str) -> None: + document["project"]["version"] = version # type: ignore[index] diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py index cee687c15b..ca00f05e7b 100644 --- a/commitizen/providers/cargo_provider.py +++ b/commitizen/providers/cargo_provider.py @@ -1,9 +1,18 @@ from __future__ import annotations -import tomlkit +import fnmatch +import glob +from pathlib import Path +from typing import TYPE_CHECKING + +from tomlkit import TOMLDocument, dumps, parse +from tomlkit.exceptions import NonExistentKey from commitizen.providers.base_provider import TomlProvider +if TYPE_CHECKING: + from tomlkit.items import AoT + class CargoProvider(TomlProvider): """ @@ -13,18 +22,84 @@ class CargoProvider(TomlProvider): """ filename = "Cargo.toml" + lock_filename = "Cargo.lock" - def get(self, document: tomlkit.TOMLDocument) -> str: - try: - return document["package"]["version"] # type: ignore - except tomlkit.exceptions.NonExistentKey: - ... - return document["workspace"]["package"]["version"] # type: ignore + @property + def lock_file(self) -> Path: + return Path() / self.lock_filename + + def get(self, document: TOMLDocument) -> str: + out = _try_get_workspace(document)["package"]["version"] + if TYPE_CHECKING: + assert isinstance(out, str) + return out + + def set(self, document: TOMLDocument, version: str) -> None: + _try_get_workspace(document)["package"]["version"] = version + + def set_version(self, version: str) -> None: + super().set_version(version) + if self.lock_file.exists(): + self.set_lock_version(version) + + def set_lock_version(self, version: str) -> None: + cargo_toml_content = parse(self.file.read_text()) + cargo_lock_content = parse(self.lock_file.read_text()) + packages = cargo_lock_content["package"] + + if TYPE_CHECKING: + assert isinstance(packages, AoT) - def set(self, document: tomlkit.TOMLDocument, version: str): try: - document["workspace"]["package"]["version"] = version # type: ignore - return - except tomlkit.exceptions.NonExistentKey: - ... - document["package"]["version"] = version # type: ignore + cargo_package_name = cargo_toml_content["package"]["name"] # type: ignore[index] + if TYPE_CHECKING: + assert isinstance(cargo_package_name, str) + for i, package in enumerate(packages): + if package["name"] == cargo_package_name: + cargo_lock_content["package"][i]["version"] = version # type: ignore[index] + break + except NonExistentKey: + workspace = cargo_toml_content.get("workspace", {}) + if TYPE_CHECKING: + assert isinstance(workspace, dict) + workspace_members = workspace.get("members", []) + excluded_workspace_members = workspace.get("exclude", []) + members_inheriting: list[str] = [] + + for member in workspace_members: + for path in glob.glob(member, recursive=True): + if any( + fnmatch.fnmatch(path, pattern) + for pattern in excluded_workspace_members + ): + continue + + cargo_file = Path(path) / "Cargo.toml" + package_content = parse(cargo_file.read_text()).get("package", {}) + if TYPE_CHECKING: + assert isinstance(package_content, dict) + try: + version_workspace = package_content["version"]["workspace"] + if version_workspace is True: + package_name = package_content["name"] + if TYPE_CHECKING: + assert isinstance(package_name, str) + members_inheriting.append(package_name) + except NonExistentKey: + pass + + for i, package in enumerate(packages): + if package["name"] in members_inheriting: + cargo_lock_content["package"][i]["version"] = version # type: ignore[index] + + self.lock_file.write_text(dumps(cargo_lock_content)) + + +def _try_get_workspace(document: TOMLDocument) -> dict: + try: + workspace = document["workspace"] + if TYPE_CHECKING: + assert isinstance(workspace, dict) + return workspace + except NonExistentKey: + return document diff --git a/commitizen/providers/commitizen_provider.py b/commitizen/providers/commitizen_provider.py index a1da25ff72..f90d7d9b6e 100644 --- a/commitizen/providers/commitizen_provider.py +++ b/commitizen/providers/commitizen_provider.py @@ -1,5 +1,6 @@ from __future__ import annotations +from commitizen.exceptions import NoVersionSpecifiedError from commitizen.providers.base_provider import VersionProvider @@ -9,7 +10,9 @@ class CommitizenProvider(VersionProvider): """ def get_version(self) -> str: - return self.config.settings["version"] # type: ignore + if ret := self.config.settings["version"]: + return ret + raise NoVersionSpecifiedError() - def set_version(self, version: str): + def set_version(self, version: str) -> None: self.config.set_key("version", version) diff --git a/commitizen/providers/npm_provider.py b/commitizen/providers/npm_provider.py index 12900ff7de..7aeb0ee7df 100644 --- a/commitizen/providers/npm_provider.py +++ b/commitizen/providers/npm_provider.py @@ -2,10 +2,13 @@ import json from pathlib import Path -from typing import Any, ClassVar +from typing import TYPE_CHECKING, Any, ClassVar from commitizen.providers.base_provider import VersionProvider +if TYPE_CHECKING: + from collections.abc import Mapping + class NpmProvider(VersionProvider): """ @@ -58,8 +61,8 @@ def set_version(self, version: str) -> None: json.dumps(shrinkwrap_document, indent=self.indent) + "\n" ) - def get_package_version(self, document: dict[str, Any]) -> str: - return document["version"] # type: ignore + def get_package_version(self, document: Mapping[str, str]) -> str: + return document["version"] def set_package_version( self, document: dict[str, Any], version: str diff --git a/commitizen/providers/poetry_provider.py b/commitizen/providers/poetry_provider.py index 7aa28f56d9..d9a174a0a4 100644 --- a/commitizen/providers/poetry_provider.py +++ b/commitizen/providers/poetry_provider.py @@ -1,9 +1,12 @@ from __future__ import annotations -import tomlkit +from typing import TYPE_CHECKING from commitizen.providers.base_provider import TomlProvider +if TYPE_CHECKING: + import tomlkit + class PoetryProvider(TomlProvider): """ @@ -13,7 +16,7 @@ class PoetryProvider(TomlProvider): filename = "pyproject.toml" def get(self, pyproject: tomlkit.TOMLDocument) -> str: - return pyproject["tool"]["poetry"]["version"] # type: ignore + return pyproject["tool"]["poetry"]["version"] # type: ignore[index,return-value] - def set(self, pyproject: tomlkit.TOMLDocument, version: str): - pyproject["tool"]["poetry"]["version"] = version # type: ignore + def set(self, pyproject: tomlkit.TOMLDocument, version: str) -> None: + pyproject["tool"]["poetry"]["version"] = version # type: ignore[index] diff --git a/commitizen/providers/scm_provider.py b/commitizen/providers/scm_provider.py index cb575148cb..687775da30 100644 --- a/commitizen/providers/scm_provider.py +++ b/commitizen/providers/scm_provider.py @@ -18,11 +18,9 @@ def get_version(self) -> str: rules = TagRules.from_settings(self.config.settings) tags = get_tags(reachable_only=True) version_tags = rules.get_version_tags(tags) - versions = sorted(rules.extract_version(t) for t in version_tags) - if not versions: - return "0.0.0" - return str(versions[-1]) + version = max((rules.extract_version(t) for t in version_tags), default=None) + return str(version) if version is not None else "0.0.0" - def set_version(self, version: str): + def set_version(self, version: str) -> None: # Not necessary pass diff --git a/commitizen/providers/uv_provider.py b/commitizen/providers/uv_provider.py index 36c8a49ad3..4574dc4b6b 100644 --- a/commitizen/providers/uv_provider.py +++ b/commitizen/providers/uv_provider.py @@ -1,19 +1,16 @@ from __future__ import annotations from pathlib import Path -from typing import TYPE_CHECKING import tomlkit +import tomlkit.items from commitizen.providers.base_provider import TomlProvider -if TYPE_CHECKING: - import tomlkit.items - class UvProvider(TomlProvider): """ - uv.lock and pyproject.tom version management + uv.lock and pyproject.toml version management """ filename = "pyproject.toml" diff --git a/commitizen/question.py b/commitizen/question.py new file mode 100644 index 0000000000..393e386092 --- /dev/null +++ b/commitizen/question.py @@ -0,0 +1,32 @@ +from typing import Callable, Literal, TypedDict, Union + + +class Choice(TypedDict, total=False): + value: str + name: str + key: str + + +class ListQuestion(TypedDict, total=False): + type: Literal["list"] + name: str + message: str + choices: list[Choice] + use_shortcuts: bool + + +class InputQuestion(TypedDict, total=False): + type: Literal["input"] + name: str + message: str + filter: Callable[[str], str] + + +class ConfirmQuestion(TypedDict): + type: Literal["confirm"] + name: str + message: str + default: bool + + +CzQuestion = Union[ListQuestion, InputQuestion, ConfirmQuestion] diff --git a/commitizen/tags.py b/commitizen/tags.py index 5724bb2574..68c74a72e6 100644 --- a/commitizen/tags.py +++ b/commitizen/tags.py @@ -2,9 +2,9 @@ import re import warnings -from collections.abc import Sequence from dataclasses import dataclass, field from functools import cached_property +from itertools import chain from string import Template from typing import TYPE_CHECKING, NamedTuple @@ -21,6 +21,7 @@ if TYPE_CHECKING: import sys + from collections.abc import Iterable, Sequence from commitizen.version_schemes import VersionScheme @@ -88,18 +89,22 @@ class TagRules: ignored_tag_formats: Sequence[str] = field(default_factory=list) merge_prereleases: bool = False + @property + def tag_formats(self) -> Iterable[str]: + return chain([self.tag_format], self.legacy_tag_formats) + @cached_property - def version_regexes(self) -> Sequence[re.Pattern]: + def version_regexes(self) -> list[re.Pattern]: """Regexes for all legit tag formats, current and legacy""" - tag_formats = [self.tag_format, *self.legacy_tag_formats] - regexes = (self._format_regex(p) for p in tag_formats) - return [re.compile(r) for r in regexes] + return [re.compile(self._format_regex(f)) for f in self.tag_formats] @cached_property - def ignored_regexes(self) -> Sequence[re.Pattern]: + def ignored_regexes(self) -> list[re.Pattern]: """Regexes for known but ignored tag formats""" - regexes = (self._format_regex(p, star=True) for p in self.ignored_tag_formats) - return [re.compile(r) for r in regexes] + return [ + re.compile(self._format_regex(f, star=True)) + for f in self.ignored_tag_formats + ] def _format_regex(self, tag_pattern: str, star: bool = False) -> str: """ @@ -113,6 +118,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 +129,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: @@ -131,8 +140,8 @@ def is_ignored_tag(self, tag: str | GitTag) -> bool: return any(regex.match(tag) for regex in self.ignored_regexes) def get_version_tags( - self, tags: Sequence[GitTag], warn: bool = False - ) -> Sequence[GitTag]: + self, tags: Iterable[GitTag], warn: bool = False + ) -> list[GitTag]: """Filter in version tags and warn on unexpected tags""" return [tag for tag in tags if self.is_version_tag(tag, warn)] @@ -146,9 +155,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")) @@ -172,11 +179,7 @@ def include_in_changelog(self, tag: GitTag) -> bool: version = self.extract_version(tag) except InvalidVersion: return False - - if self.merge_prereleases and version.is_prerelease: - return False - - return True + return not (self.merge_prereleases and version.is_prerelease) def search_version(self, text: str, last: bool = False) -> VersionTag | None: """ @@ -238,15 +241,12 @@ def normalize_tag( ) def find_tag_for( - self, tags: Sequence[GitTag], version: Version | str + self, tags: Iterable[GitTag], version: Version | str ) -> GitTag | None: """Find the first matching tag for a given version.""" version = self.scheme(version) if isinstance(version, str) else version - possible_tags = [ - self.normalize_tag(version, f) - for f in (self.tag_format, *self.legacy_tag_formats) - ] - candidates = [t for t in tags if any(t.name == p for p in possible_tags)] + possible_tags = set(self.normalize_tag(version, f) for f in self.tag_formats) + candidates = [t for t in tags if t.name in possible_tags] if len(candidates) > 1: warnings.warn( UserWarning( diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py index 2486be58c8..0c71bb06cc 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 @@ -41,7 +41,9 @@ Increment: TypeAlias = Literal["MAJOR", "MINOR", "PATCH"] Prerelease: TypeAlias = Literal["alpha", "beta", "rc"] -DEFAULT_VERSION_PARSER = r"v?(?P<version>([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.]+)?(\w+)?)" +_DEFAULT_VERSION_PARSER = re.compile( + r"v?(?P<version>([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.]+)?(\w+)?)" +) @runtime_checkable @@ -49,7 +51,7 @@ class VersionProtocol(Protocol): parser: ClassVar[re.Pattern] """Regex capturing this version scheme into a `version` group""" - def __init__(self, version: str): + def __init__(self, version: str) -> None: """ Initialize a version object from its string representation. @@ -78,7 +80,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 +144,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. """ @@ -156,7 +158,7 @@ class BaseVersion(_BaseVersion): A base class implementing the `VersionProtocol` for PEP440-like versions. """ - parser: ClassVar[re.Pattern] = re.compile(DEFAULT_VERSION_PARSER) + parser: ClassVar[re.Pattern] = _DEFAULT_VERSION_PARSER """Regex capturing this version scheme into a `version` group""" @property @@ -264,40 +266,36 @@ def bump( if self.local and is_local_version: local_version = self.scheme(self.local).bump(increment) - return self.scheme(f"{self.public}+{local_version}") # type: ignore - else: - if not self.is_prerelease: - base = self.increment_base(increment) - elif exact_increment: - base = self.increment_base(increment) - else: - base = f"{self.major}.{self.minor}.{self.micro}" - if increment == PATCH: - pass - elif increment == MINOR: - if self.micro != 0: - base = self.increment_base(increment) - elif increment == MAJOR: - if self.minor != 0 or self.micro != 0: - base = self.increment_base(increment) - dev_version = self.generate_devrelease(devrelease) - - release = list(self.release) - if len(release) < 3: - release += [0] * (3 - len(release)) - current_base = ".".join(str(part) for part in release) - if base == current_base: - pre_version = self.generate_prerelease( - prerelease, offset=prerelease_offset - ) - else: - base_version = cast(BaseVersion, self.scheme(base)) - pre_version = base_version.generate_prerelease( - prerelease, offset=prerelease_offset - ) - build_metadata = self.generate_build_metadata(build_metadata) - # TODO: post version - return self.scheme(f"{base}{pre_version}{dev_version}{build_metadata}") # type: ignore + return self.scheme(f"{self.public}+{local_version}") # type: ignore[return-value] + + base = self._get_increment_base(increment, exact_increment) + dev_version = self.generate_devrelease(devrelease) + + release = list(self.release) + if len(release) < 3: + release += [0] * (3 - len(release)) + current_base = ".".join(str(part) for part in release) + + pre_version = ( + self if base == current_base else cast("BaseVersion", self.scheme(base)) + ).generate_prerelease(prerelease, offset=prerelease_offset) + + # TODO: post version + return self.scheme( + f"{base}{pre_version}{dev_version}{self.generate_build_metadata(build_metadata)}" + ) # type: ignore[return-value] + + def _get_increment_base( + self, increment: Increment | None, exact_increment: bool + ) -> str: + if ( + not self.is_prerelease + or exact_increment + or (increment == MINOR and self.micro != 0) + or (increment == MAJOR and (self.minor != 0 or self.micro != 0)) + ): + return self.increment_base(increment) + return f"{self.major}.{self.minor}.{self.micro}" class Pep440(BaseVersion): @@ -316,7 +314,7 @@ class SemVer(BaseVersion): """ def __str__(self) -> str: - parts = [] + parts: list[str] = [] # Epoch if self.epoch != 0: @@ -351,7 +349,7 @@ class SemVer2(SemVer): See: https://semver.org/spec/v2.0.0.html """ - _STD_PRELEASES = { + _STD_PRERELEASES = { "a": "alpha", "b": "beta", } @@ -359,12 +357,12 @@ 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 def __str__(self) -> str: - parts = [] + parts: list[str] = [] # Epoch if self.epoch != 0: @@ -373,9 +371,19 @@ def __str__(self) -> str: # Release segment parts.append(".".join(str(x) for x in self.release)) + if prerelease := self._get_prerelease(): + parts.append(f"-{prerelease}") + + # Local version segment + if self.local: + parts.append(f"+{self.local}") + + return "".join(parts) + + def _get_prerelease(self) -> str: # Pre-release identifiers # See: https://semver.org/spec/v2.0.0.html#spec-item-9 - prerelease_parts = [] + prerelease_parts: list[str] = [] if self.prerelease: prerelease_parts.append(f"{self.prerelease}") @@ -387,15 +395,7 @@ def __str__(self) -> str: if self.dev is not None: prerelease_parts.append(f"dev.{self.dev}") - if prerelease_parts: - parts.append("-") - parts.append(".".join(prerelease_parts)) - - # Local version segment - if self.local: - parts.append(f"+{self.local}") - - return "".join(parts) + return ".".join(prerelease_parts) DEFAULT_SCHEME: VersionScheme = Pep440 @@ -410,7 +410,9 @@ def __str__(self) -> str: def get_version_scheme(settings: Settings, name: str | None = None) -> VersionScheme: """ Get the version scheme as defined in the configuration - or from an overridden `name` + or from an overridden `name`. + + :raises VersionSchemeUnknown: if the version scheme is not found. """ @@ -419,7 +421,7 @@ def get_version_scheme(settings: Settings, name: str | None = None) -> VersionSc if deprecated_setting: warnings.warn( DeprecationWarning( - "`version_type` setting is deprecated and will be removed in commitizen 4. " + "`version_type` setting is deprecated and will be removed in v5. " "Please use `version_scheme` instead" ) ) @@ -431,7 +433,7 @@ def get_version_scheme(settings: Settings, name: str | None = None) -> VersionSc (ep,) = metadata.entry_points(name=name, group=SCHEMES_ENTRYPOINT) except ValueError: raise VersionSchemeUnknown(f'Version scheme "{name}" unknown.') - scheme = cast(VersionScheme, ep.load()) + scheme = cast("VersionScheme", ep.load()) if not isinstance(scheme, VersionProtocol): warnings.warn(f"Version scheme {name} does not implement the VersionProtocol") diff --git a/docs/README.md b/docs/README.md index 128602dfb3..fa3fab2239 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,168 +8,311 @@ [![Codecov](https://img.shields.io/codecov/c/github/commitizen-tools/commitizen.svg?style=flat-square)](https://codecov.io/gh/commitizen-tools/commitizen) [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?style=flat-square&logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) -![Using commitizen cli](images/demo.gif) +![Using Commitizen cli](images/demo.gif) --- -**Documentation:** [https://commitizen-tools.github.io/commitizen/](https://commitizen-tools.github.io/commitizen/) +[**Commitizen Documentation Site**](https://commitizen-tools.github.io/commitizen/) --- ## About -Commitizen is release management tool designed for teams. +Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management. -Commitizen assumes your team uses a standard way of committing rules -and from that foundation, it can bump your project's version, create -the changelog, and update files. +### What Commitizen Does -By default, commitizen uses [conventional commits][conventional_commits], but you -can build your own set of rules, and publish them. +By enforcing standardized commit conventions (defaulting to [Conventional Commits][conventional_commits]), Commitizen helps teams: -Using a standardized set of rules to write commits, makes commits easier to read, and enforces writing -descriptive commits. +- Write clear, structured commit messages +- Automatically manage version numbers using semantic versioning +- Generate and maintain changelogs +- Streamline the release process + +### Key Benefits + +With just a simple `cz bump` command, Commitizen handles: + +1. **Version Management**: Automatically bumps version numbers and updates version files based on your commit history +2. **Changelog Generation**: Creates and updates changelogs following the [Keep a changelog][keepchangelog] format +3. **Commit Standardization**: Enforces consistent commit message formats across your team + +This standardization makes your commit history more readable and meaningful, while the automation reduces manual work and potential errors in the release process. ### Features -- Command-line utility to create commits with your rules. Defaults: [Conventional commits][conventional_commits] -- Bump version automatically using [semantic versioning][semver] based on the commits. [Read More](./commands/bump.md) -- Generate a changelog using [Keep a changelog][keepchangelog] -- Update your project's version files automatically -- Display information about your commit rules (commands: schema, example, info) -- Create your own set of rules and publish them to pip. Read more on [Customization](./customization.md) +- Interactive CLI for standardized commits with default [Conventional Commits][conventional_commits] support +- Intelligent [version bumping](https://commitizen-tools.github.io/commitizen/commands/bump/) using [Semantic Versioning][semver] +- Automatic [keep a changelog][keepchangelog] generation +- Built-in commit validation with pre-commit hooks +- [Customizable](https://commitizen-tools.github.io/commitizen/customization/) commit rules and templates +- Multi-format version file support +- Custom rules and plugins via pip + +## Getting Started -## Requirements +### Requirements -[Python](https://www.python.org/downloads/) `3.9+` +Before installing Commitizen, ensure you have: -[Git][gitscm] `1.8.5.2+` +- [Python](https://www.python.org/downloads/) `3.9+` +- [Git][gitscm] `1.8.5.2+` -## Installation +### Installation -Install commitizen in your system using `pipx` (Recommended, <https://pypa.github.io/pipx/installation/>): +#### Global Installation (Recommended) +The recommended way to install Commitizen is using [`pipx`](https://pipx.pypa.io/) or [`uv`](https://docs.astral.sh/uv/), which ensures a clean, isolated installation: + +**Using pipx:** ```bash -pipx ensurepath +# Install Commitizen pipx install commitizen + +# Keep it updated pipx upgrade commitizen ``` -Install commitizen using `pip` with `--user` flag: - +**Using uv:** ```bash -pip install --user -U commitizen +# Install commitizen +uv tool install commitizen + +# Keep it updated +uv tool upgrade commitizen ``` -### Python project +**(For macOS users) Using Homebrew:** +```bash +brew install commitizen +``` -You can add it to your local project using one of the following. +#### Project-Specific Installation -With `pip`: +You can add Commitizen to your Python project using any of these package managers: +**Using pip:** ```bash pip install -U commitizen ``` -With `conda`: - +**Using conda:** ```bash conda install -c conda-forge commitizen ``` -With Poetry >= 1.2.0: - +**Using Poetry:** ```bash +# For Poetry >= 1.2.0 poetry add commitizen --group dev + +# For Poetry < 1.2.0 +poetry add commitizen --dev ``` -With Poetry < 1.2.0: +**Using uv:** +```bash +uv add --dev commitizen +``` +**Using pdm:** ```bash -poetry add commitizen --dev +pdm add -d commitizen ``` -### macOS +### Basic Commands -via [homebrew](https://formulae.brew.sh/formula/commitizen): +#### Initialize Commitizen -```bash -brew install commitizen +To get started, you'll need to set up your configuration. You have two options: + +1. Use the interactive setup: +```sh +cz init ``` -## Usage +2. Manually create a configuration file (`.cz.toml` or `cz.toml`): +```toml +[tool.commitizen] +version = "0.1.0" +update_changelog_on_bump = true +``` -Most of the time this is the only command you'll run: +#### Create Commits +Create standardized commits using: +```sh +cz commit +# or use the shortcut +cz c +``` + +To sign off your commits: +```sh +cz commit -- --signoff +# or use the shortcut +cz commit -- -s +``` + +For more commit options, run `cz commit --help`. + +#### Version Management + +The most common command you'll use is: ```sh cz bump ``` -On top of that, you can use commitizen to assist you with the creation of commits: +This command: + +- Bumps your project's version +- Creates a git tag +- Updates the changelog (if `update_changelog_on_bump` is enabled) +- Updates version files + +You can customize: + +- [Version files](https://commitizen-tools.github.io/commitizen/commands/bump/#version_files) +- [Version scheme](https://commitizen-tools.github.io/commitizen/commands/bump/#version_scheme) +- [Version provider](https://commitizen-tools.github.io/commitizen/config/#version-providers) + +For all available options, see the [bump command documentation](https://commitizen-tools.github.io/commitizen/commands/bump/). + +### Advanced Usage + +#### Get Project Version + +```sh +# Get your project's version (instead of Commitizen's version) +cz version -p +# Preview changelog changes +cz changelog --dry-run "$(cz version -p)" +``` + +This command is particularly useful for automation scripts and CI/CD pipelines. + +For example, you can use the output of the command `cz changelog --dry-run "$(cz version -p)"` to notify your team about a new release in Slack. +#### Pre-commit Integration + +Commitizen can automatically validate your commit messages using pre-commit hooks. + +1. Add to your `.pre-commit-config.yaml`: +```yaml +--- +repos: + - repo: https://github.com/commitizen-tools/commitizen + rev: master # Replace with latest tag + hooks: + - id: commitizen + - id: commitizen-branch + stages: [pre-push] +``` + +2. Install the hooks: ```sh -cz commit +pre-commit install --hook-type commit-msg --hook-type pre-push ``` -Read more in the section [Getting Started](./getting_started.md). +| Hook | Recommended Stage | +| ----------------- | ----------------- | +| commitizen | commit-msg | +| commitizen-branch | pre-push | + +> **Note**: Replace `master` with the [latest tag](https://github.com/commitizen-tools/commitizen/tags) to avoid warnings. You can automatically update this with: +> ```sh +> pre-commit autoupdate +> ``` + +For more details about commit validation, see the [check command documentation](https://commitizen-tools.github.io/commitizen/commands/check/). + +## Help & Reference -### Help +### Command Line Interface + +Commitizen provides a comprehensive CLI with various commands. Here's the complete reference: + +![cz --help](images/cli_help/cz___help.svg) + +### Quick Reference + +| Command | Description | Alias | +|---------|-------------|-------| +| `cz init` | Initialize Commitizen configuration | - | +| `cz commit` | Create a new commit | `cz c` | +| `cz bump` | Bump version and update changelog | - | +| `cz changelog` | Generate changelog | `cz ch` | +| `cz check` | Validate commit messages | - | +| `cz version` | Show version information | - | + +### Additional Resources + +- [Conventional Commits Specification][conventional_commits] +- [Exit Codes Reference](https://commitizen-tools.github.io/commitizen/exit_codes/) +- [Configuration Guide](https://commitizen-tools.github.io/commitizen/config/) +- [Command Documentation](https://commitizen-tools.github.io/commitizen/commands/init/) + +### Getting Help + +For each command, you can get detailed help by adding `--help`: ```sh -$ cz --help -usage: cz [-h] [--debug] [-n NAME] [-nr NO_RAISE] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... - -Commitizen is a cli tool to generate conventional commits. -For more information about the topic go to https://conventionalcommits.org/ - -optional arguments: - -h, --help show this help message and exit - --config the path of configuration file - --debug use debug mode - -n NAME, --name NAME use the given commitizen (default: cz_conventional_commits) - -nr NO_RAISE, --no-raise NO_RAISE - comma separated error codes that won't rise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen- - tools.github.io/commitizen/exit_codes/ - -commands: - {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} - init init commitizen configuration - commit (c) create new commit - ls show available commitizens - example show commit example - info show information about the cz - schema show commit schema - bump bump semantic version based on the git log - changelog (ch) generate changelog (note that it will overwrite existing file) - check validates that a commit message matches the commitizen schema - version get the version of the installed commitizen or the current project (default: installed commitizen) +cz commit --help +cz bump --help +cz changelog --help ``` +For more details, visit our [documentation site](https://commitizen-tools.github.io/commitizen/). + ## Setting up bash completion -When using bash as your shell (limited support for zsh, fish, and tcsh is available), Commitizen can use [argcomplete](https://kislyuk.github.io/argcomplete/) for auto-completion. For this argcomplete needs to be enabled. +Commitizen supports command-line completion through [argcomplete](https://kislyuk.github.io/argcomplete/), which is automatically installed as a dependency. This feature provides intelligent auto-completion for all Commitizen commands and options. -argcomplete is installed when you install Commitizen since it's a dependency. +### Supported Shells -If Commitizen is installed globally, global activation can be executed: +- **Bash**: Full support +- **Zsh**: Limited support +- **Fish**: Limited support +- **Tcsh**: Limited support + +### Installation Methods + +#### Global Installation (Recommended) + +If you installed Commitizen globally (e.g., using `pipx` or `brew`), you can enable global completion: ```bash +# Enable global completion for all Python applications sudo activate-global-python-argcomplete ``` -For permanent (but not global) Commitizen activation, use: +#### User-Specific Installation + +For a user-specific installation that persists across sessions: ```bash +# Add to your shell's startup file (e.g., ~/.bashrc, ~/.zshrc) register-python-argcomplete cz >> ~/.bashrc ``` -For one-time activation of argcomplete for Commitizen only, use: +#### Temporary Installation + +For one-time activation in your current shell session: ```bash +# Activate completion for current session only eval "$(register-python-argcomplete cz)" ``` -For further information on activation, please visit the [argcomplete website](https://kislyuk.github.io/argcomplete/). +### Verification + +After installation, you can verify the completion is working by: + +1. Opening a new terminal session +2. Typing `cz` followed by a space and pressing `TAB` twice +3. You should see a list of available commands + +For more detailed information about argcomplete configuration and troubleshooting, visit the [argcomplete documentation](https://kislyuk.github.io/argcomplete/). ## Sponsors diff --git a/docs/commands/bump.md b/docs/commands/bump.md index efdba76257..e20a20d5f7 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -2,59 +2,116 @@ ## About -`cz bump` **automatically** increases the version, based on the commits. +`cz bump` is a powerful command that **automatically** determines and increases your project's version number based on your commit history. -The commits should follow the rules established by the committer in order to be parsed correctly. +It analyzes your commits to determine the appropriate version increment according to semantic versioning principles. -**prerelease** versions are supported (alpha, beta, release candidate). +!!! note + In the following documentation, the term "configuration file" refers to `pyproject.toml`, `.cz.toml` or other configuration files. + + We will use `pyproject.toml` as the configuration file throughout the documentation. + + See [Configuration file](../config/configuration_file.md) for more details. + +## Key Features -The version can also be **manually** bumped. +- **Automatic Version Detection**: Analyzes commit history to determine the appropriate version bump +- **Manual Version Control**: Supports manual version specification when needed +- **Pre-release Support**: Handles alpha, beta, and release candidate versions +- **Multiple Version Schemes**: Supports both [PEP 440][pep440] and [semantic versioning][semver] formats -The version format follows [PEP 0440][pep440] and [semantic versioning][semver]. +### Version Increment Rules -This means `MAJOR.MINOR.PATCH` +The version follows the `MAJOR.MINOR.PATCH` format, with increments determined by your commit types: | Increment | Description | Conventional commit map | | --------- | --------------------------- | ----------------------- | -| `MAJOR` | Breaking changes introduced | `BREAKING CHANGE` | +| `MAJOR` | Breaking changes introduced | `BREAKING CHANGE`, bang (e.g. `feat!`)| | `MINOR` | New features | `feat` | -| `PATCH` | Fixes | `fix` + everything else | +| `PATCH` | Fixes and improvements | `fix`, `perf`, `refactor`| -[PEP 0440][pep440] is the default, you can switch by using the setting `version_scheme` or the cli: +### `--version-scheme` + +By default, Commitizen uses [PEP 440][pep440] for version formatting. You can switch to semantic versioning using either: +1. Command line: ```sh cz bump --version-scheme semver ``` -Some examples of pep440: +2. Configuration file: +```toml title="pyproject.toml" +[tool.commitizen] +version_scheme = "semver" +``` -```bash -0.9.0 -0.9.1 -0.9.2 -0.9.10 -0.9.11 -1.0.0a0 # alpha -1.0.0a1 -1.0.0b0 # beta -1.0.0rc0 # release candidate -1.0.0rc1 -1.0.0 -1.0.1 -1.1.0 -2.0.0 -2.0.1a +Available options are: + +- `pep440`: [PEP 440][pep440] (**default** and recommended for Python projects) +- `semver`: [Semantic Versioning][semver] (recommended for non-Python projects) + +You can also set this in the configuration file with `version_scheme = "semver"`. + +!!! note + [pep440][pep440] and [semver][semver] are quite similar, although their difference lies in + how the prereleases look. For example, `0.3.1a0` in pep440 is equivalent to `0.3.1-a0` in semver. + + The following table illustrates the difference between the two schemes: + + | Version Type | pep440 | semver | + |--------------|----------------|-----------------| + | Non-prerelease | `0.1.0` | `0.1.0` | + | Prerelease | `0.3.1a0` | `0.3.1-a0` | + | Devrelease | `0.1.1.dev1` | `0.1.1-dev1` | + | Dev and pre | `1.0.0a3.dev1` | `1.0.0-a3-dev1` | + +### PEP440 Version Examples + +Commitizen supports the [PEP 440][pep440] version format, which includes several version types. Here are examples of each: + +#### Standard Releases +```text +0.9.0 # Initial development release +0.9.1 # Patch release +0.9.2 # Another patch release +0.9.10 # Tenth patch release +0.9.11 # Eleventh patch release +1.0.0 # First stable release +1.0.1 # Patch release after stable +1.1.0 # Minor feature release +2.0.0 # Major version release ``` -`post` releases are not supported yet. +#### Pre-releases +```text +1.0.0a0 # Alpha release 0 +1.0.0a1 # Alpha release 1 +1.0.0b0 # Beta release 0 +1.0.0rc0 # Release candidate 0 +1.0.0rc1 # Release candidate 1 +``` -## Usage +#### Development Releases +```text +1.0.0.dev0 # Development release 0 +1.0.0.dev1 # Development release 1 +``` + +#### Combined Pre-release and Development +```text +1.0.0a1.dev0 # Development release 0 of alpha 1 +1.0.0b2.dev1 # Development release 1 of beta 2 +``` + +> **Note**: `post` releases (e.g., `1.0.0.post1`) are not currently supported. + +## Command line options ![cz bump --help](../images/cli_help/cz_bump___help.svg) ### `--files-only` -Bumps the version in the files defined in `version_files` without creating a commit and tag on the git repository, +Bumps the version in the files defined in [`version_files`][version_files] without creating a commit and tag on the git repository. ```bash cz bump --files-only @@ -62,7 +119,7 @@ cz bump --files-only ### `--changelog` -Generate a **changelog** along with the new version and tag when bumping. +Generate a **changelog** along with the new version and tag when bumping. See [changelog](./changelog.md) for more details. ```bash cz bump --changelog @@ -71,8 +128,8 @@ cz bump --changelog ### `--prerelease` The bump is a pre-release bump, meaning that in addition to a possible version bump the new version receives a -pre-release segment compatible with the bump’s version scheme, where the segment consist of a _phase_ and a -non-negative number. Supported options for `--prerelease` are the following phase names `alpha`, `beta`, or +pre-release segment compatible with the bump's version scheme, where the segment consists of a _phase_ and a +non-negative number. Supported options for `--prerelease` are the following phase names `alpha`, `beta`, or `rc` (release candidate). For more details, refer to the [Python Packaging User Guide](https://packaging.python.org/en/latest/specifications/version-specifiers/#pre-releases). @@ -85,7 +142,7 @@ Note that as per [semantic versioning spec](https://semver.org/#spec-item-9) For example, the following versions (using the [PEP 440](https://peps.python.org/pep-0440/) scheme) are ordered by their precedence and showcase how a release might flow through a development cycle: -- `1.0.0` is the current published version +- `1.0.0` is the currently published version - `1.0.1a0` after committing a `fix:` for pre-release - `1.1.0a1` after committing an additional `feat:` for pre-release - `1.1.0b0` after bumping a beta release @@ -94,16 +151,21 @@ by their precedence and showcase how a release might flow through a development ### `--increment-mode` -By default, `--increment-mode` is set to `linear`, which ensures that bumping pre-releases _maintains linearity_: -bumping of a pre-release with lower precedence than the current pre-release phase maintains the current phase of -higher precedence. For example, if the current version is `1.0.0b1` then bumping with `--prerelease alpha` will -continue to bump the “beta” phase. +#### `--increment-mode=linear` (default) + +Ensures that bumping pre-releases **maintains linearity**. -Setting `--increment-mode` to `exact` instructs `cz bump` to instead apply the -exact changes that have been specified with `--increment` or determined from the commit log. For example, -`--prerelease beta` will always result in a `b` tag, and `--increment PATCH` will always increase the patch component. +Bumping a pre-release with lower precedence than the current pre-release phase maintains the current phase of higher precedence. +For example, if the current version is `1.0.0b1` then bumping with `--prerelease alpha` will continue to bump the *beta* phase. -Below are some examples that illustrate the difference in behavior: +#### `--increment-mode=exact` + +Applies the exact changes that have been specified with `--increment` or determined from the commit log. +For example, `--prerelease beta` will always result in a `b` tag, and `--increment PATCH` will always increase the patch component. + +#### Examples + +The following table illustrates the difference in behavior between the two modes: | Increment | Pre-release | Start Version | `--increment-mode=linear` | `--increment-mode=exact` | |-----------|-------------|---------------|---------------------------|--------------------------| @@ -116,16 +178,15 @@ Below are some examples that illustrate the difference in behavior: ### `--check-consistency` -Check whether the versions defined in `version_files` and the version in commitizen -configuration are consistent before bumping version. +Check whether the versions defined in [version_files][version_files] and the version in Commitizen configuration are consistent before bumping version. ```bash cz bump --check-consistency ``` -For example, if we have `pyproject.toml` +For example, if we have the following configuration file `pyproject.toml`: -```toml +```toml title="pyproject.toml" [tool.commitizen] version = "1.21.0" version_files = [ @@ -134,127 +195,172 @@ version_files = [ ] ``` -`src/__version__.py`, +and the following version files `src/__version__.py` and `setup.py`: -```python +```python title="src/__version__.py" __version__ = "1.21.0" ``` -and `setup.py`. - -```python +```python title="setup.py" from setuptools import setup setup(..., version="1.0.5", ...) ``` -If `--check-consistency` is used, commitizen will check whether the current version in `pyproject.toml` -exists in all version_files and find out it does not exist in `setup.py` and fails. -However, it will still update `pyproject.toml` and `src/__version__.py`. +When you run `cz bump --check-consistency`, Commitizen will verify that the current version in `pyproject.toml` (`1.21.0`) exists in all files listed in [version_files][version_files]. +In this example, it will detect that `setup.py` contains `1.0.5` instead of `1.21.0`, causing the bump to fail. + +!!! warning "Partial updates on failure" + If the consistency check fails, Commitizen may have already updated some files (like `pyproject.toml` and `src/__version__.py`) before detecting the inconsistency. + In this case, you'll need to restore the files to their previous state. + + To resolve this issue: -To fix it, you'll first `git checkout .` to reset to the status before trying to bump and update -the version in `setup.py` to `1.21.0` + 1. Restore the modified files to their previous state: + ```bash + git checkout . + ``` + + 2. Manually update the version in `setup.py` to match the version in `pyproject.toml`: + ```diff title="setup.py" + from setuptools import setup + + - setup(..., version="1.0.5", ...) + + setup(..., version="1.21.0", ...) + ``` + + 3. Run the bump command again: + ```bash + cz bump --check-consistency + ``` ### `--local-version` Bump the local portion of the version. -```bash -cz bump --local-version -``` - -For example, if we have `pyproject.toml` +For example, if we have the following configuration file `pyproject.toml`: -```toml +```toml title="pyproject.toml" [tool.commitizen] version = "5.3.5+0.1.0" ``` -If `--local-version` is used, it will bump only the local version `0.1.0` and keep the public version `5.3.5` intact, bumping to the version `5.3.5+0.2.0`. +When you run `cz bump --local-version`, it will bump only the local version `0.1.0` and keep the public version `5.3.5` intact, bumping to the version `5.3.5+0.2.0`. ### `--annotated-tag` -If `--annotated-tag` is used, commitizen will create annotated tags. Also available via configuration, in `pyproject.toml` or `.cz.toml`. +Create annotated tags. + +It is also available via configuration files. + +For example, in `pyproject.toml`: + +```toml title="pyproject.toml" +[tool.commitizen] +annotated_tag = true +``` + +!!! note + By default, Commitizen uses lightweight tags. ### `--annotated-tag-message` -If `--annotated-tag-message` is used, commitizen will create annotated tags with the given message. +Create annotated tags with the given message. + +It is also available via configuration files. + +For example, in `pyproject.toml`: + +```toml title="pyproject.toml" +[tool.commitizen] +annotated_tag_message = "Annotated tag message" +``` ### `--changelog-to-stdout` -If `--changelog-to-stdout` is used, the incremental changelog generated by the bump -will be sent to the stdout, and any other message generated by the bump will be -sent to stderr. +Send the incremental changelog generated by `cz bump` to `stdout`. +Any other messages generated by `cz bump` will be sent to `stderr`. -If `--changelog` is not used with this command, it is still smart enough to -understand that the user wants to create a changelog. It is recommended to be -explicit and use `--changelog` (or the setting `update_changelog_on_bump`). +When this flag is used, `--changelog` is implied. +However, it is recommended to set `--changelog` (or the setting `update_changelog_on_bump`) explicitly when the option `--changelog-to-stdout` is used. -This command is useful to "transport" the newly created changelog. -It can be sent to an auditing system, or to create a Github Release. +!!! note "Useful scenarios" + Pipe the newly created changelog to another tool. -Example: + The output can be redirected to an auditing system, or used to create a GitHub Release, etc. -```bash -cz bump --changelog --changelog-to-stdout > body.md -``` + ```bash + cz bump --changelog --changelog-to-stdout > body.md + ``` ### `--git-output-to-stderr` -If `--git-output-to-stderr` is used, git commands output is redirected to stderr. +Redirects git commands output to `stderr`. + +Useful when used with `--changelog-to-stdout` and piping the output to a file. -This command is useful when used with `--changelog-to-stdout` and piping the output to a file, -and you don't want the `git commit` output polluting the stdout. +For example, `git commit` output may pollute `stdout`, so it is recommended to use this flag when piping the output to a file. ### `--retry` -If you use tools like [pre-commit](https://pre-commit.com/), add this flag. -It will retry the commit if it fails the 1st time. +If you use tools like [pre-commit](https://pre-commit.com/), you can add this flag. +It will retry the commit if it fails the first time. Useful to combine with code formatters, like [Prettier](https://prettier.io/). ### `--major-version-zero` -A project in its initial development should have a major version zero, and even breaking changes -should not bump that major version from zero. This command ensures that behavior. +Breaking changes do not bump the major version number. -If `--major-version-zero` is used for projects that have a version number greater than zero it fails. -If used together with a manual version the command also fails. +Say you have a project with the version `0.1.x` and you commit a breaking change like this: -We recommend setting `major_version_zero = true` in your configuration file while a project -is in its initial development. Remove that configuration using a breaking-change commit to bump -your project’s major version to `v1.0.0` once your project has reached maturity. +```text +fix(magic)!: fully deprecate whatever +``` -### `--version-scheme` +and you run -Choose the version format, options: `pep440`, `semver`. +```bash +cz bump --major-version-zero +``` + +Then the version of your project will be bumped to `0.2.0` instead of `1.0.0`. + +!!! note + A project in its initial development should have a major version zero, + and even breaking changes should not bump that major version from zero. This command ensures that behavior. + + We recommend setting `major_version_zero = true` in your configuration file while a project + is in its initial development. Remove that configuration using a breaking-change commit to bump + your project's major version to `v1.0.0` once your project has reached maturity. -Default: `pep440` +!!! warning + This option is only compatible with projects that have major version number zero, `0.x.x` for example. -Recommended for python: `pep440` + It fails when used with projects that have a version number greater than zero like `1.x.x`. -Recommended for other: `semver` + If used together with a manual version, the command also fails. -You can also set this in the [configuration](#version_scheme) with `version_scheme = "semver"`. + ```bash + # This fails + cz bump 0.1.0 --major-version-zero + ``` -[pep440][pep440] and [semver][semver] are quite similar, their difference lies in -how the prereleases look. +### `--gpg-sign` -| schemes | pep440 | semver | -| -------------- | -------------- | --------------- | -| non-prerelease | `0.1.0` | `0.1.0` | -| prerelease | `0.3.1a0` | `0.3.1-a0` | -| devrelease | `0.1.1.dev1` | `0.1.1-dev1` | -| dev and pre | `1.0.0a3.dev1` | `1.0.0-a3-dev1` | +Creates gpg signed tags. -Can I transition from one to the other? +```bash +cz bump --gpg-sign +``` -Yes, you shouldn't have any issues. +!!! note + By default, Commitizen uses lightweight tags. ### `--template` Provides your own changelog jinja template. -See [the template customization section](../customization.md#customizing-the-changelog-template) +See [the template customization section](../customization/changelog_template.md) ### `--extra` @@ -264,152 +370,111 @@ Provides your own changelog extra variables by using the `extras` settings or th cz bump --changelog --extra key=value -e short="quoted value" ``` -See [the template customization section](../customization.md#customizing-the-changelog-template). +See [the template customization section](../customization/changelog_template.md). ### `--build-metadata` -Provides a way to specify additional metadata in the version string. This parameter is not compatible with `--local-version` as it uses the same part of the version string. +Specifies additional metadata in the version string. ```bash +# creates a version like `1.1.2+yourmetadata`. cz bump --build-metadata yourmetadata ``` -Will create a version like `1.1.2+yourmetadata`. -This can be useful for multiple things -- Git hash in version -- Labeling the version with additional metadata. - -Note that Commitizen ignores everything after `+` when it bumps the version. It is therefore safe to write different build-metadata between versions. - -You should normally not use this functionality, but if you decide to do, keep in mind that -- Version `1.2.3+a`, and `1.2.3+b` are the same version! Tools should not use the string after `+` for version calculation. This is probably not a guarantee (example in helm) even tho it is in the spec. -- It might be problematic having the metadata in place when doing upgrades depending on what tool you use. - -### `--get-next` - -Provides a way to determine the next version and write it to stdout. This parameter is not compatible with `--changelog` -and `manual version`. - -```bash -cz bump --get-next -``` - -Will output the next version, e.g., `1.2.3`. This can be useful for determining the next version based on CI for non -production environments/builds. - -This behavior differs from the `--dry-run` flag. The `--dry-run` flag provides a more detailed output and can also show -the changes as they would appear in the changelog file. +!!! note "Example usage" + - Git hash in version + - Labeling the version with additional metadata. -The following output is the result of `cz bump --dry-run`: +!!! note + Commitizen ignores everything after `+` when it bumps the version. -``` -bump: version 3.28.0 → 3.29.0 -tag to create: v3.29.0 -increment detected: MINOR -``` + It is therefore safe to write different build-metadata between versions. -The following output is the result of `cz bump --get-next`: -``` -3.29.0 -``` - -The `--get-next` flag will raise a `NoneIncrementExit` if the found commits are not eligible for a version bump. +!!! warning + Normally, you should not use this functionality, but if you decide to do so, keep in mind that: -For information on how to suppress this exit, see [avoid raising errors](#avoid-raising-errors). + - Version `1.2.3+a`, and `1.2.3+b` are the same version! Tools should not use the string after `+` for version calculation. This is probably not a guarantee (example in helm) even tho it is in the spec. + - It might be problematic having the metadata in place when doing upgrades depending on what tool you use. -### `--allow-no-commit` +!!! warning + This parameter is not compatible with `--local-version` as it uses the same part of the version string. -Allow the project version to be bumped even when there's no eligible version. This is most useful when used with `--increment {MAJOR,MINOR,PATCH}` or `[MANUL_VERSION]` +### `--get-next` -```sh -# bump a minor version even when there's only bug fixes, documentation changes or even no commits -cz bump --incremental MINOR --allow-no-commit +Similar to `--dry-run` but only outputs the next version. -# bump version to 2.0.0 even when there's no breaking changes changes or even no commits -cz bump --allow-no-commit 2.0.0 +```bash +# outputs 1.0.1 if the current version is 1.0.0 and the increment is PATCH +cz bump --get-next ``` -## Avoid raising errors +Useful for determining the next version based on CI for non-production environments/builds. -Some situations from commitizen raise an exit code different than 0. -If the error code is different than 0, any CI or script running commitizen might be interrupted. +!!! note "Compare with `--dry-run`" + `--dry-run` provides a more detailed output including the changes as they would appear in the changelog file, while `--get-next` only outputs the next version. -If you have a special use case, where you don't want to raise one of this error codes, you can -tell commitizen to not raise them. + The following is the output of `cz bump --dry-run`: -### Recommended use case + ```text + bump: version 3.28.0 → 3.29.0 + tag to create: v3.29.0 + increment detected: MINOR + ``` -At the moment, we've identified that the most common error code to skip is + The following is the output of `cz bump --get-next`: -| Error name | Exit code | -| ----------------- | --------- | -| NoneIncrementExit | 21 | + ```text + 3.29.0 + ``` -There are some situations where you don't want to get an error code when some -commits do not match your rules, you just want those commits to be skipped. +!!! warning + The `--get-next` flag will raise a `NoneIncrementExit` if the found commits are not eligible for a version bump. -```sh -cz -nr 21 bump -``` - -### Easy way - -Check which error code was raised by commitizen by running in the terminal - -```sh -echo $? -``` + For information on how to suppress this exit, see [Ignoring Exit Codes](../exit_codes.md#ignoring-exit-codes). -The output should be an integer like this +### `--allow-no-commit` -```sh -3 -``` +Allow the project version to be bumped even when there's no eligible version. -And then you can tell commitizen to ignore it: +Example usage: ```sh -cz --no-raise 3 -``` - -You can tell commitizen to skip more than one if needed: +# Force to bump a minor version +cz bump --increment MINOR --allow-no-commit -```sh -cz --no-raise 3,4,5 +# bump version to 2.0.0 even when there's no breaking changes or even no commits +cz bump --allow-no-commit 2.0.0 ``` -### Longer way - -Check the list of [exit_codes](../exit_codes.md) and understand which one you have -to skip and why. +!!! note "Default increment" + The increment is overridden to `PATCH` if there is no increment detected or specified. -Remember to document somewhere this, because you'll forget. + In other words, `cz bump --allow-no-commit` allows you to bump the version to the next patch version even when there is no eligible commit. -For example if the system raises a `NoneIncrementExit` error, you look it up -on the list and then you can use the exit code: + ```sh + # will bump to `1.0.1` if the current version is `1.0.0`. + cz bump --allow-no-commit -```sh -cz -nr 21 bump -``` + # bump version to 2.0.0 even when there's no breaking changes or even no commits + cz bump --allow-no-commit 2.0.0 + ``` -## Configuration +### `--tag-format` -### `tag_format` - -`tag_format` and `version_scheme` are combined to make Git tag names from versions. +`tag_format` and [version_scheme][version_scheme] are combined to make Git tag names from versions. These are used in: - `cz bump`: Find previous release tag (exact match) and generate new tag. - Find previous release tags in `cz changelog`. - - If `--incremental`: Using latest version found in the changelog, scan existing Git tags with 89\% similarity match. + - If `--incremental`: Using the latest version found in the changelog, scan existing Git tags with 89\% similarity match. - `--rev-range` is converted to Git tag names with `tag_format` before searching Git history. - If the `scm` `version_provider` is used, it uses different regexes to find the previous version tags: - If `tag_format` is set to `$version` (default): `VersionProtocol.parser` (allows `v` prefix) - If `tag_format` is set: Custom regex similar to SemVer (not as lenient as PEP440 e.g. on dev-releases) -Commitizen supports 2 types of formats, a simple and a more complex. +Commitizen supports two types of formats, a simple and a more complex. ```bash cz bump --tag-format="v$version" @@ -419,218 +484,26 @@ cz bump --tag-format="v$version" cz bump --tag-format="v$minor.$major.$patch$prerelease.$devrelease" ``` -In your `pyproject.toml` or `.cz.toml` +In your configuration file: ```toml [tool.commitizen] tag_format = "v$major.$minor.$patch$prerelease" ``` -The variables must be preceded by a `$` sign and optionally can be wrapped in `{}` . Default is `$version`. +The variables must be preceded by a `$` sign and optionally can be wrapped in `{}`. The default is `$version`. Supported variables: | Variable | Description | |--------------------------------|---------------------------------------------| -| `$version`, `${version}` | full generated version | +| `$version`, `${version}` | fully generated version | | `$major`, `${major}` | MAJOR increment | | `$minor`, `${minor}` | MINOR increment | | `$patch`, `${patch}` | PATCH increment | | `$prerelease`, `${prerelease}` | Prerelease (alpha, beta, release candidate) | -| `$devrelease`, ${devrelease}` | Development release | - ---- - -### `version_files` \* - -It is used to identify the files which should be updated with the new version. -It is also possible to provide a pattern for each file, separated by colons (`:`). - -Commitizen will update its configuration file automatically (`pyproject.toml`, `.cz`) when bumping, -regarding if the file is present or not in `version_files`. - -\* Renamed from `files` to `version_files`. - -Some examples - -`pyproject.toml`, `.cz.toml` or `cz.toml` - -```toml -[tool.commitizen] -version_files = [ - "src/__version__.py", - "setup.py:version" -] -``` - -In the example above, we can see the reference `"setup.py:version"`. -This means that it will find a file `setup.py` and will only make a change -in a line containing the `version` substring. - -!!! note - Files can be specified using relative (to the execution) paths, absolute paths - or glob patterns. - ---- - -### `bump_message` - -Template used to specify the commit message generated when bumping. - -defaults to: `bump: version $current_version → $new_version` - -| Variable | Description | -| ------------------ | ----------------------------------- | -| `$current_version` | the version existing before bumping | -| `$new_version` | version generated after bumping | - -Some examples - -`pyproject.toml`, `.cz.toml` or `cz.toml` - -```toml -[tool.commitizen] -bump_message = "release $current_version → $new_version [skip-ci]" -``` - ---- - -### `update_changelog_on_bump` - -When set to `true` the changelog is always updated incrementally when running `cz bump`, so the user does not have to provide the `--changelog` flag every time. - -defaults to: `false` - -```toml -[tool.commitizen] -update_changelog_on_bump = true -``` - ---- - -### `annotated_tag` - -When set to `true` commitizen will create annotated tags. - -```toml -[tool.commitizen] -annotated_tag = true -``` - ---- - -### `gpg_sign` - -When set to `true` commitizen will create gpg signed tags. - -```toml -[tool.commitizen] -gpg_sign = true -``` - ---- - -### `major_version_zero` - -When set to `true` commitizen will keep the major version at zero. -Useful during the initial development stage of your project. - -Defaults to: `false` - -```toml -[tool.commitizen] -major_version_zero = true -``` - ---- - -### `pre_bump_hooks` - -A list of optional commands that will run right _after_ updating `version_files` -and _before_ actual committing and tagging the release. - -Useful when you need to generate documentation based on the new version. During -execution of the script, some environment variables are available: - -| Variable | Description | -| ---------------------------- | ---------------------------------------------------------- | -| `CZ_PRE_IS_INITIAL` | `True` when this is the initial release, `False` otherwise | -| `CZ_PRE_CURRENT_VERSION` | Current version, before the bump | -| `CZ_PRE_CURRENT_TAG_VERSION` | Current version tag, before the bump | -| `CZ_PRE_NEW_VERSION` | New version, after the bump | -| `CZ_PRE_NEW_TAG_VERSION` | New version tag, after the bump | -| `CZ_PRE_MESSAGE` | Commit message of the bump | -| `CZ_PRE_INCREMENT` | Whether this is a `MAJOR`, `MINOR` or `PATH` release | -| `CZ_PRE_CHANGELOG_FILE_NAME` | Path to the changelog file, if available | - -```toml -[tool.commitizen] -pre_bump_hooks = [ - "scripts/generate_documentation.sh" -] -``` - ---- - -### `post_bump_hooks` - -A list of optional commands that will run right _after_ committing and tagging the release. - -Useful when you need to send notifications about a release, or further automate deploying the -release. During execution of the script, some environment variables are available: - -| Variable | Description | -| ------------------------------ | ----------------------------------------------------------- | -| `CZ_POST_WAS_INITIAL` | `True` when this was the initial release, `False` otherwise | -| `CZ_POST_PREVIOUS_VERSION` | Previous version, before the bump | -| `CZ_POST_PREVIOUS_TAG_VERSION` | Previous version tag, before the bump | -| `CZ_POST_CURRENT_VERSION` | Current version, after the bump | -| `CZ_POST_CURRENT_TAG_VERSION` | Current version tag, after the bump | -| `CZ_POST_MESSAGE` | Commit message of the bump | -| `CZ_POST_INCREMENT` | Whether this was a `MAJOR`, `MINOR` or `PATH` release | -| `CZ_POST_CHANGELOG_FILE_NAME` | Path to the changelog file, if available | - -```toml -[tool.commitizen] -post_bump_hooks = [ - "scripts/slack_notification.sh" -] -``` - -### `prerelease_offset` - -Offset with which to start counting prereleases. - -Defaults to: `0` - -```toml -[tool.commitizen] -prerelease_offset = 1 -``` - -### `version_scheme` - -Choose version scheme - -| schemes | pep440 | semver | semver2 | -| -------------- | -------------- | --------------- | --------------------- | -| non-prerelease | `0.1.0` | `0.1.0` | `0.1.0` | -| prerelease | `0.3.1a0` | `0.3.1-a0` | `0.3.1-alpha.0` | -| devrelease | `0.1.1.dev1` | `0.1.1-dev1` | `0.1.1-dev.1` | -| dev and pre | `1.0.0a3.dev1` | `1.0.0-a3-dev1` | `1.0.0-alpha.3.dev.1` | - -Options: `pep440`, `semver`, `semver2` - -Defaults to: `pep440` - -```toml -[tool.commitizen] -version_scheme = "semver" -``` - -## Custom bump - -Read the [customizing section](../customization.md). +| `$devrelease`, `${devrelease}` | Development release | [pep440]: https://www.python.org/dev/peps/pep-0440/ [semver]: https://semver.org/ +[version_files]: ../config/bump.md#version_files diff --git a/docs/commands/changelog.md b/docs/commands/changelog.md index cbf22b15a7..3f2425a51f 100644 --- a/docs/commands/changelog.md +++ b/docs/commands/changelog.md @@ -2,44 +2,37 @@ This command will generate a changelog following the committing rules established. -To create the changelog automatically on bump, add the setting [update_changelog_on_bump](./bump.md#update_changelog_on_bump) +!!! tip + To create the changelog automatically on bump, add the setting [update_changelog_on_bump](../config/bump.md#update_changelog_on_bump) -```toml -[tool.commitizen] -update_changelog_on_bump = true -``` + ```toml + [tool.commitizen] + update_changelog_on_bump = true + ``` ## Usage ![cz changelog --help](../images/cli_help/cz_changelog___help.svg) -### Examples - -#### Generate full changelog +## Examples ```bash +# Generate full changelog cz changelog -``` -```bash +# or use the alias cz ch -``` -#### Get the changelog for the given version - -```bash +# Get the changelog for the given version cz changelog 0.3.0 --dry-run -``` - -#### Get the changelog for the given version range -```bash +# Get the changelog for the given version range cz changelog 0.3.0..0.4.0 --dry-run ``` ## Constrains -changelog generation is constrained only to **markdown** files. +Changelog generation is constrained only to **markdown** files. ## Description @@ -56,7 +49,7 @@ These are the variables used by the changelog generator. It will create a full block like above per version found in the tags. And it will create a list of the commits found. The `change_type` and the `scope` are optional, they don't need to be provided, -but if your regex does they will be rendered. +but if your regex does, they will be rendered. The format followed by the changelog is the one from [keep a changelog][keepachangelog] and the following variables are expected: @@ -72,55 +65,33 @@ and the following variables are expected: - **required**: is the only one required to be parsed by the regex -## Configuration - -### `unreleased_version` - -There is usually a chicken and egg situation when automatically -bumping the version and creating the changelog. -If you bump the version first, you have no changelog, you have to -create it later, and it won't be included in -the release of the created version. - -If you create the changelog before bumping the version, then you -usually don't have the latest tag, and the _Unreleased_ title appears. - -By introducing `unreleased_version` you can prevent this situation. - -Before bumping you can run: - -```bash -cz changelog --unreleased-version="v1.0.0" -``` - -Remember to use the tag instead of the raw version number +## Command line options -For example if the format of your tag includes a `v` (`v1.0.0`), then you should use that, -if your tag is the same as the raw version, then ignore this. +### `--extras` -Alternatively you can directly bump the version and create the changelog by doing +Provides your own changelog extra variables by using the `extras` settings or the `--extra/-e` parameter. ```bash -cz bump --changelog +cz changelog --extra key=value -e short="quoted value" ``` -### `file-name` +### `--file-name` -This value can be updated in the `toml` file with the key `changelog_file` under `tools.commitizen` +This value can be updated in the configuration file with the key `changelog_file` under `tools.commitizen` -Specify the name of the output file, remember that changelog only works with markdown. +Specify the name of the output file, remember that changelog only works with Markdown. ```bash cz changelog --file-name="CHANGES.md" ``` -### `incremental` +### `--incremental` -This flag can be set in the `toml` file with the key `changelog_incremental` under `tools.commitizen` +This flag can be set in the configuration file with the key `changelog_incremental` under `tools.commitizen` Benefits: -- Build from latest version found in changelog, this is useful if you have a different changelog and want to use commitizen +- Build from the latest version found in changelog, this is useful if you have a different changelog and want to use commitizen - Update unreleased area - Allows users to manually touch the changelog without being rewritten. @@ -134,9 +105,9 @@ cz changelog --incremental changelog_incremental = true ``` -### `start-rev` +### `--start-rev` -This value can be set in the `toml` file with the key `changelog_start_rev` under `tools.commitizen` +This value can be set in the configuration file with the key `changelog_start_rev` under `tools.commitizen` Start from a given git rev to generate the changelog. Commits before that rev will not be considered. This is especially useful for long-running projects adopting conventional commits, where old commit messages might fail to be parsed for changelog generation. @@ -150,9 +121,9 @@ cz changelog --start-rev="v0.2.0" changelog_start_rev = "v0.2.0" ``` -### merge-prerelease +### `--merge-prerelease` -This flag can be set in the `toml` file with the key `changelog_merge_prerelease` under `tools.commitizen` +This flag can be set in the configuration file with the key `changelog_merge_prerelease` under `tools.commitizen` Collects changes from prereleases into the next non-prerelease. This means that if you have a prerelease version, and then a normal release, the changelog will show the prerelease changes as part of the changes of the normal release. If not set, it will include prereleases in the changelog. @@ -166,30 +137,49 @@ cz changelog --merge-prerelease changelog_merge_prerelease = true ``` -### `template` +### `--template` Provides your own changelog jinja template by using the `template` settings or the `--template` parameter. -See [the template customization section](../customization.md#customizing-the-changelog-template) -### `extras` +### `--unreleased-version` -Provides your own changelog extra variables by using the `extras` settings or the `--extra/-e` parameter. +There is usually a chicken and egg situation when automatically +bumping the version and creating the changelog. +If you bump the version first, you have no changelog, you have to +create it later, and it won't be included in +the release of the created version. + +If you create the changelog before bumping the version, then you +usually don't have the latest tag, and the _Unreleased_ title appears. + +By introducing `--unreleased-version` you can prevent this situation. + +Before bumping you can run: ```bash -cz changelog --extra key=value -e short="quoted value" +cz changelog --unreleased-version="v1.0.0" ``` -See [the template customization section](../customization.md#customizing-the-changelog-template) +Remember to use the tag instead of the raw version number + +For example if the format of your tag includes a `v` (`v1.0.0`), then you should use that, +if your tag is the same as the raw version, then ignore this. + +Alternatively you can directly bump the version and create the changelog by doing + +```bash +cz bump --changelog +``` ## Hooks Supported hook methods: -- per parsed message: useful to add links -- end of changelog generation: useful to send slack or chat message, or notify another department +- Per parsed message: Useful to add links +- End of changelog generation: Useful to send Slack or chat messages, or notify another department Read more about hooks in the [customization page][customization] [keepachangelog]: https://keepachangelog.com/ [semver]: https://semver.org/ -[customization]: ../customization.md +[customization]: ../customization/config_file.md diff --git a/docs/commands/check.md b/docs/commands/check.md index e45ecd86c8..ef7f8f5f90 100644 --- a/docs/commands/check.md +++ b/docs/commands/check.md @@ -1,87 +1,164 @@ -# Check +This feature checks whether a string or a range of git commits follows the given committing rules. Comments in git messages will be ignored. -## About - -This feature checks whether the commit message follows the given committing rules. And comment in git message will be ignored. - -If you want to setup an automatic check before every git commit, please refer to -[Automatically check message before commit](../tutorials/auto_check.md). +To set up an automatic check before every git commit, please refer to [Automatically check message before commit](../tutorials/auto_check.md). ## Usage ![cz check --help](../images/cli_help/cz_check___help.svg) -There are three mutually exclusive ways to use `cz check`: +More specifically, there are three mutually exclusive ways to use `cz check`: + +- Validate a range of git commit messages with `--rev-range` +- Validate a given string with `--message` or by piping the message to it +- Validate a commit message from a file with `--commit-msg-file` -- with `--rev-range` to check a range of pre-existing commits -- with `--message` or by piping the message to it to check a given string -- or with `--commit-msg-file` to read the commit message from a file +### Use `cz check` to validate a commit message before committing -### Git Rev Range +#### Option 1: use `--message` to check a given string: -If you'd like to check a commit's message after it has already been created, then you can specify the range of commits to check with `--rev-range REV_RANGE`. +```bash +cz check --message <message_to_be_checked> +``` +#### Option 2: pipe the message to `cz check`: ```bash -$ cz check --rev-range REV_RANGE +echo <message_to_be_checked> | cz check ``` -For example, if you'd like to check all commits on a branch, you can use `--rev-range master..HEAD`. Or, if you'd like to check all commits starting from when you first implemented commit message linting, you can use `--rev-range <first_commit_sha>..HEAD`. +#### Option 3: use `--commit-msg-file` to read the commit message from a file +```bash +cz check --commit-msg-file /path/to/file.txt +``` + +## Command Line Options + +### `--rev-range` + +Test if a given range of commits in the git log passes `cz check`. + +```bash +cz check --rev-range REV_RANGE +``` + +For more information on `REV_RANGE`, check the [git documentation](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#_commit_ranges). + +#### Use cases + +1. Validate the latest 3 commit messages: + ```bash + cz check --rev-range HEAD~3..HEAD + # or + cz check --rev-range HEAD~3.. + # or + cz check --rev-range HEAD~~~.. + ``` + +1. Validate all git commit messages on some branch up to HEAD: + ```bash + cz check --rev-range <branch_name>..HEAD + ``` -For more info on how git commit ranges work, you can check the [git documentation](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#_commit_ranges). + For example, to check all git commit messages on `main` branch up to HEAD: + ```bash + cz check --rev-range main..HEAD + ``` -### Commit Message + or if your project still uses `master` branch: + ```bash + cz check --rev-range master..HEAD + ``` -There are two ways you can provide your plain message and check it. + !!! note "Default branch" + Usually the default branch is `main` or `master`. + You can check the default branch by running `cz check --use-default-range`. -#### Method 1: use -m or --message +1. Validate all git commit messages starting from when you first implemented commit message linting: + + **(Why this is useful?)** Let's say you decided to enforce commit message today. However, it is impractical to `git rebase` all your previous commits. `--rev-range` helps you skip commits before you first implemented commit message linting by using a specific commit hash. + + ```bash + cz check --rev-range <first_commit_sha>..HEAD + ``` + +### `--use-default-range` + +Equivalent to `--rev-range <default_branch>..HEAD`. ```bash -$ cz check --message MESSAGE +cz check --use-default-range +# or +cz check -d ``` -In this option, MESSAGE is the commit message to be checked. +### `--message` -#### Method 2: use pipe to pipe it to `cz check` +Test if a given string passes `cz check`. ```bash -$ echo MESSAGE | cz check +cz check --message <message_to_be_checked> ``` -In this option, MESSAGE is piped to cz check and would be checked. +### `--commit-msg-file` -### Commit Message File +Test if a given file contains a commit message that passes `cz check`. ```bash -$ cz check --commit-msg-file COMMIT_MSG_FILE +cz check --commit-msg-file <path_to_file_containing_message_to_be_checked> ``` -In this option, COMMIT_MSG_FILE is the path of the temporal file that contains the commit message. -This argument can be useful when cooperating with git hook, please check [Automatically check message before commit](../tutorials/auto_check.md) for more information about how to use this argument with git hook. +This can be useful when cooperating with git hooks. Please check [Automatically check message before commit](../tutorials/auto_check.md) for more detailed examples. + +### `--allow-abort` -### Allow Abort +Example: ```bash -cz check --message MESSAGE --allow-abort +cz check --message <message_to_be_checked> --allow-abort ``` Empty commit messages typically instruct Git to abort a commit, so you can pass `--allow-abort` to -permit them. Since `git commit` accepts an `--allow-empty-message` flag (primarily for wrapper scripts), you may wish to disallow such commits in CI. `--allow-abort` may be used in conjunction with any of the other options. +permit them. Since `git commit` accepts the `--allow-empty-message` flag (primarily for wrapper scripts), you may wish to disallow such commits in CI. `--allow-abort` may be used in conjunction with any of the other options. -### Allowed Prefixes +### `--allowed-prefixes` -If the commit message starts by some specific prefixes, `cz check` returns `True` without checkign the regex. -By default, the the following prefixes are allowed: `Merge`, `Revert`, `Pull request`, `fixup!` and `squash!`. +Skip validation for commit messages that start with the specified prefixes. + +If not set, commit messages starting with the following prefixes are ignored by `cz check`: + +- `Merge` +- `Revert` +- `Pull request` +- `fixup!` +- `squash!` +- `amend!` ```bash -cz check --message MESSAGE --allowed-prefixes 'Merge' 'Revert' 'Custom Prefix' +cz check --message <message_to_be_checked> --allowed-prefixes 'Merge' 'Revert' 'Custom Prefix' ``` -### Commit message length limit +For example, + +```bash +# The following message passes the check because it starts with 'Merge' +cz check --message "Merge branch 'main' into feature/new-feature" --allowed-prefixes 'Merge' + +# The following fails +cz check --message "Merge branch 'main' into feature/new-feature" --allowed-prefixes 'aaa' +``` + +### `--message-length-limit` + +Restrict the length of **the first line** of the commit message. + +```bash +# The following passes +cz check --message "docs(check): fix grammar issues" -l 80 + +# The following fails +cz check --message "docs:very long long long long message with many words" -l 3 +``` -The argument `-l` (or `--message-length-limmit`) followed by a positive number, can limit the length of commit messages. -For example, `cz check --message MESSAGE -l 3` would fail the check, since `MESSAGE` is more than 3 characters long. -By default, the limit is set to 0, which means no limit on the length. +By default, the limit is set to `0`, which means no limit on the length. -**Note that the limit applies only to the first line of the message.*** -Specifically, for `ConventionalCommitsCz` the length only counts from the type of change to the subject, -while the body, and the footer are not counted. +!!! note + Specifically, for `ConventionalCommitsCz` the length only counts from the type of change to the subject, while the body and the footer are not counted. diff --git a/docs/commands/commit.md b/docs/commands/commit.md index 7760a2b88e..54e0c8b07a 100644 --- a/docs/commands/commit.md +++ b/docs/commands/commit.md @@ -1,52 +1,65 @@ -![Using commitizen cli](../images/demo.gif) +## Usage -## About +![cz commit --help](../images/cli_help/cz_commit___help.svg) -In your terminal run `cz commit` or the shortcut `cz c` to generate a guided git commit. +## Overview -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). +![Using Commitizen cli](../images/demo.gif) +The `commit` command provides an interactive way to create structured commits. Use either: -!!! 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). +- `cz commit` +- `cz c` (shortcut) -## Usage +By default, Commitizen uses conventional commits, but you can customize the commit rules to match your project's needs. See the [customization guide](../customization/config_file.md) for details. -![cz commit --help](../images/cli_help/cz_commit___help.svg) +## Basic Usage -### git options +### Interactive Commit Creation -`git` command options that are not implemented by commitizen can be use via the `--` syntax for the `commit` command. -The syntax separates commitizen arguments from `git commit` arguments by a double dash. This is the resulting syntax: +Simply run `cz commit` in your terminal to start the interactive commit creation process. The command will guide you through creating a properly formatted commit message according to your configured rules. + +### Writing Messages to File + +You can save the generated commit message to a file using: +```sh +cz commit --write-message-to-file COMMIT_MSG_FILE +``` + +This can be combined with `--dry-run` to only write the message without creating a commit. This is particularly useful for [automatically preparing commit messages](../tutorials/auto_prepare_commit_message.md). + +## Advanced Features + +### Git Command Options + +You can pass any git commit options using the `--` syntax: ```sh cz commit <commitizen-args> -- <git-cli-args> -# e.g., cz commit --dry-run -- -a -S +# Examples: +cz c --dry-run -- -a -S # Stage all changes and sign the commit +cz c -a -- -n # Stage all changes and skip the pre-commit and commit-msg hooks ``` -For example, using the `-S` option on `git commit` to sign a commit is now commitizen compatible: `cz c -- -S` -!!! note - Deprecation warning: A commit can be signed off using `cz commit --signoff` or the shortcut `cz commit -s`. - This syntax is now deprecated in favor of the new `cz commit -- -s` syntax. +!!! warning + The `--signoff` option (or `-s`) is now recommended being used with the new syntax: `cz commit -- -s`. The old syntax `cz commit --signoff` is deprecated and will be removed in v5. ### Retry -You can use `cz commit --retry` to reuse the last commit message when the previous commit attempt failed. -To automatically retry when running `cz commit`, you can set the `retry_after_failure` -configuration option to `true`. Running `cz commit --no-retry` makes commitizen ignore `retry_after_failure`, forcing -a new commit message to be prompted. +- Use `cz commit --retry` to reuse the last commit message after a failed commit attempt +- Set `retry_after_failure: true` in your configuration to automatically retry +- Use `cz commit --no-retry` to force a new commit message prompt + +### Message Length Control -### Commit message length limit +Control the length of your commit messages using the `-l` or `--message-length-limit` option: +```sh +cz commit -l 72 # Limits message length to 72 characters +``` + +!!! note + The length limit only applies to the first line of the commit message. For conventional commits, this means the limit applies from the type of change through the subject. The body and footer are not counted. -The argument `-l` (or `--message-length-limit`) followed by a positive number can limit the length of commit messages. -An exception would be raised when the message length exceeds the limit. -For example, `cz commit -l 72` will limit the length of commit messages to 72 characters. -By default the limit is set to 0, which means no limit on the length. +## Technical Notes -**Note that the limit applies only to the first line of the message.** -Specifically, for `ConventionalCommitsCz` the length only counts from the type of change to the subject, -while the body and the footer are not counted. +For platform compatibility, the `commit` command disables ANSI escaping in its output. This means 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/commands/init.md b/docs/commands/init.md index 01e1db6be8..b673ba1276 100644 --- a/docs/commands/init.md +++ b/docs/commands/init.md @@ -1,27 +1,68 @@ +The `cz init` command helps you set up Commitizen in your project by creating a configuration file with your preferred settings. + ## Usage ![cz init --help](../images/cli_help/cz_init___help.svg) -## Example - -To start using commitizen, the recommended approach is to run +## Command ```sh cz init ``` +## Interactive Configuration + +When you run `cz init`, Commitizen will guide you through an interactive setup process: + ![init](../images/init.gif) -This command will ask you for information about the project and will -configure the selected file type (`pyproject.toml`, `.cz.toml`, etc.). +## Configuration File + +The initialization process will create a configuration file in your project root. + +See [Configuration File][configuration_file] for more details. + +## Configuration Options + +During the initialization process, you'll be prompted to configure the following settings: + +1. **Convention Rules**: Select the commit message convention to follow (e.g., conventional commits) +2. **Version Provider**: Choose how to manage versioning in your project. Commitizen supports multiple version management systems: + - `commitizen`: Uses Commitizen's built-in version management system + - `npm`: Manages version in `package.json` for Node.js projects + - `cargo`: Manages version in `Cargo.toml` for Rust projects + - `composer`: Manages version in `composer.json` for PHP projects + - `pep621`: Uses `pyproject.toml` with PEP 621 standard + - `poetry`: Uses `pyproject.toml` with Poetry configuration + - `uv`: Uses `pyproject.toml` and `uv.lock` for Python projects + - `scm`: Reads version directly from git tags without modifying files +3. **Project Version**: The current version of your project will be detected automatically +4. **Tag Format**: The format used for version tags in your repository +5. **Version Type**: Choose between: + - `semver` or `semver2`: Semantic Versioning (MAJOR.MINOR.PATCH) + - `pep440`: Python Package Versioning +6. **Changelog Generation**: Configure whether to automatically generate changelog during version bumps +7. **Alpha Versioning**: Option to keep major version at 0 for alpha/beta software +8. **Pre-commit Hooks**: Set up Git pre-commit hooks for automated commit message validation + +See [Configuration Options][configuration_options] for more details. + +## Example + +```sh +# Start the initialization process +cz init + +# Follow the interactive prompts to configure your project +``` + +## Next Steps + +After initialization, you can: -The `init` will help you with +1. Start using [`cz commit`](./commit.md) to create conventional commits +2. Use [`cz bump`](./bump.md) to manage versioning +3. Configure additional settings in your project's [configuration_file][configuration_file] -1. Choose a convention rules (`name`) -2. Choosing a version provider (`commitizen` or for example `Cargo.toml`) -3. Detecting your project's version -4. Detecting the tag format used -5. Choosing a version type (`semver` or `pep440`) -6. Whether to create the changelog automatically or not during bump -7. Whether you want to keep the major as zero while building alpha software. -8. Whether to setup pre-commit hooks. +[configuration_file]: ../config/configuration_file.md +[configuration_options]: ../config/option.md diff --git a/docs/commands/version.md b/docs/commands/version.md index 9a8176b45f..4d2e6a0323 100644 --- a/docs/commands/version.md +++ b/docs/commands/version.md @@ -1,4 +1,4 @@ -Get the version of the installed commitizen or the current project (default: installed commitizen) +Get the version of the installed Commitizen or the current project (default: installed commitizen) ## Usage diff --git a/docs/config.md b/docs/config.md deleted file mode 100644 index d1ae90b29a..0000000000 --- a/docs/config.md +++ /dev/null @@ -1,423 +0,0 @@ -# Configuration - -## Settings - -### `name` - -Type: `str` - -Default: `"cz_conventional_commits"` - -Name of the committing rules to use - -### `version` - -Type: `str` - -Default: `None` - -Current version. Example: "0.1.2". Required if you use `version_provider = "commitizen"`. - -### `version_files` - -Type: `list` - -Default: `[ ]` - -Files were the version will be updated. A pattern to match a line, can also be specified, separated by `:` [Read more][version_files] - -### `version_provider` - -Type: `str` - -Default: `commitizen` - -Version provider used to read and write version [Read more](#version-providers) - -### `version_scheme` - -Type: `str` - -Default: `pep440` - -Select a version scheme from the following options [`pep440`, `semver`, `semver2`]. -Useful for non-python projects. [Read more][version-scheme] - -### `tag_format` - -Type: `str` - -Default: `$version` - -Format for the git tag, useful for old projects, that use a convention like `"v1.2.1"`. [Read more][tag_format] - -### `legacy_tag_formats` - -Type: `list` - -Default: `[ ]` - -Legacy git tag formats, useful for old projects that changed tag format. -Tags matching those formats will be recognized as version tags and be included in the changelog. -Each entry use the the syntax as [`tag_format`](#tag_format). [Read more][tag_format] - -### `ignored_tag_formats` - -Type: `list` - -Default: `[ ]` - -Tags matching those formats will be totally ignored and won't raise a warning. -Each entry use the the syntax as [`tag_format`](#tag_format) with the addition of `*` -that will match everything (non-greedy). [Read more][tag_format] - -### `update_changelog_on_bump` - -Type: `bool` - -Default: `false` - -Create changelog when running `cz bump` - -### `gpg_sign` - -Type: `bool` - -Default: `false` - -Use gpg signed tags instead of lightweight tags. - -### `annotated_tag` - -Type: `bool` - -Default: `false` - -Use annotated tags instead of lightweight tags. [See difference][annotated-tags-vs-lightweight] - -### `bump_message` - -Type: `str` - -Default: `None` - -Create custom commit message, useful to skip ci. [Read more][bump_message] - -### `retry_after_failure` - -Type: `bool` - -Default: `false` - -Automatically retry failed commit when running `cz commit`. [Read more][retry_after_failure] - -### `allow_abort` - -Type: `bool` - -Default: `false` - -Disallow empty commit messages, useful in ci. [Read more][allow_abort] - -### `allowed_prefixes` - -Type: `list` -Default: `[ "Merge", "Revert", "Pull request", "fixup!", "squash!"]` -Allow some prefixes and do not try to match the regex when checking the message [Read more][allowed_prefixes] - -### `changelog_file` - -Type: `str` - -Default: `CHANGELOG.md` - -Filename of exported changelog - -### `changelog_format` - -Type: `str` - -Default: None - -Format used to parse and generate the changelog, If not specified, guessed from [`changelog_file`](#changelog_file). - -### `changelog_incremental` - -Type: `bool` - -Default: `false` - -Update changelog with the missing versions. This is good if you don't want to replace previous versions in the file. Note: when doing `cz bump --changelog` this is automatically set to `true` - -### `changelog_start_rev` - -Type: `str` - -Default: `None` - -Start from a given git rev to generate the changelog - -### `changelog_merge_prerelease` - -Type: `bool` - -Default: `false` - -Collect all changes of prerelease versions into the next non-prerelease version when creating the changelog. - -### `style` - -Type: `list` - -see above - -Style for the prompts (It will merge this value with default style.) [See More (Styling your prompts with your favorite colors)][additional-features] - -### `customize` - -Type: `dict` - -Default: `None` - -**This is only supported when config through `toml`.** Custom rules for committing and bumping. [Read more][customization] - -### `use_shortcuts` - -Type: `bool` - -Default: `false` - -If enabled, commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [Read more][shortcuts] - -### `major_version_zero` - -Type: `bool` - -Default: `false` - -When true, breaking changes on a `0.x` will remain as a `0.x` version. On `false`, a breaking change will bump a `0.x` version to `1.0`. [major-version-zero] - -### `prerelease_offset` - -Type: `int` - -Default: `0` - -In some circumstances, a prerelease cannot start with a 0, e.g. in an embedded project individual characters are encoded as bytes. This can be done by specifying an offset from which to start counting. [prerelease-offset] - -### `pre_bump_hooks` - -Type: `list[str]` - -Default: `[]` - -Calls the hook scripts **before** bumping version. [Read more][pre_bump_hooks] - -### `post_bump_hooks` - -Type: `list[str]` - -Default: `[]` - -Calls the hook scripts **after** bumping the version. [Read more][post_bump_hooks] - -### `encoding` - -Type: `str` - -Default: `utf-8` - -Sets the character encoding to be used when parsing commit messages. [Read more][encoding] - -### `template` - -Type: `str` - -Default: `None` (provided by plugin) - -Provide custom changelog jinja template path relative to the current working directory. [Read more][template-customization] - -### `extras` - -Type: `dict[str, Any]` - -Default: `{}` - -Provide extra variables to the changelog template. [Read more][template-customization] - -## Configuration file - -### pyproject.toml, .cz.toml or cz.toml - -Default and recommended configuration format for a project. -For a **python** project, we recommend adding an entry to your `pyproject.toml`. -You can also create a `.cz.toml` or `cz.toml` file at the root of your project folder. - -Example configuration: - -```toml -[tool.commitizen] -name = "cz_conventional_commits" -version = "0.1.0" -version_files = [ - "src/__version__.py", - "pyproject.toml:version" -] -update_changelog_on_bump = true -style = [ - ["qmark", "fg:#ff9d00 bold"], - ["question", "bold"], - ["answer", "fg:#ff9d00 bold"], - ["pointer", "fg:#ff9d00 bold"], - ["highlighted", "fg:#ff9d00 bold"], - ["selected", "fg:#cc5454"], - ["separator", "fg:#cc5454"], - ["instruction", ""], - ["text", ""], - ["disabled", "fg:#858585 italic"] -] -``` - -### .cz.json or cz.json - -Commitizen has support for JSON configuration. Recommended for `NodeJS` projects. - -```json -{ - "commitizen": { - "name": "cz_conventional_commits", - "version": "0.1.0", - "version_files": ["src/__version__.py", "pyproject.toml:version"], - "style": [ - ["qmark", "fg:#ff9d00 bold"], - ["question", "bold"], - ["answer", "fg:#ff9d00 bold"], - ["pointer", "fg:#ff9d00 bold"], - ["highlighted", "fg:#ff9d00 bold"], - ["selected", "fg:#cc5454"], - ["separator", "fg:#cc5454"], - ["instruction", ""], - ["text", ""], - ["disabled", "fg:#858585 italic"] - ] - } -} -``` - -### .cz.yaml or cz.yaml - -YAML configuration is supported by Commitizen. Recommended for `Go`, `ansible`, or even `helm` charts projects. - -```yaml -commitizen: - name: cz_conventional_commits - version: 0.1.0 - version_files: - - src/__version__.py - - pyproject.toml:version - style: - - - qmark - - fg:#ff9d00 bold - - - question - - bold - - - answer - - fg:#ff9d00 bold - - - pointer - - fg:#ff9d00 bold - - - highlighted - - fg:#ff9d00 bold - - - selected - - fg:#cc5454 - - - separator - - fg:#cc5454 - - - instruction - - "" - - - text - - "" - - - disabled - - fg:#858585 italic -``` - -## Version providers - -Commitizen can read and write version from different sources. -By default, it use the `commitizen` one which is using the `version` field from the commitizen settings. -But you can use any `commitizen.provider` entrypoint as value for `version_provider`. - -Commitizen provides some version providers for some well known formats: - -| name | description | -| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `commitizen` | Default version provider: Fetch and set version in commitizen config. | -| `scm` | Fetch the version from git and does not need to set it back | -| `pep621` | Get and set version from `pyproject.toml` `project.version` field | -| `poetry` | Get and set version from `pyproject.toml` `tool.poetry.version` field | -| `uv` | Get and set version from `pyproject.toml` `project.version` field and `uv.lock` `package.version` field whose `package.name` field is the same as `pyproject.toml` `project.name` field | -| `cargo` | Get and set version from `Cargo.toml` `project.version` field | -| `npm` | Get and set version from `package.json` `version` field, `package-lock.json` `version,packages.''.version` fields if the file exists, and `npm-shrinkwrap.json` `version,packages.''.version` fields if the file exists | -| `composer` | Get and set version from `composer.json` `project.version` field | - -!!! note -The `scm` provider is meant to be used with `setuptools-scm` or any packager `*-scm` plugin. - -An example in your `.cz.toml` or `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. - -Here a quick example of a `my-provider` provider reading and writing version in a `VERSION` file. - -```python title="my_provider.py" -from pathlib import Path -from commitizen.providers import VersionProvider - - -class MyProvider(VersionProvider): - file = Path() / "VERSION" - - def get_version(self) -> str: - return self.file.read_text() - - def set_version(self, version: str): - self.file.write_text(version) -``` - -```python title="setup.py" -from setuptools import setup - -setup( - name="my-commitizen-provider", - version="0.1.0", - py_modules=["my_provider"], - install_requires=["commitizen"], - entry_points={ - "commitizen.provider": [ - "my-provider = my_provider:MyProvider", - ] - }, -) -``` - -[version_files]: commands/bump.md#version_files -[tag_format]: commands/bump.md#tag_format -[bump_message]: commands/bump.md#bump_message -[major-version-zero]: commands/bump.md#-major-version-zero -[prerelease-offset]: commands/bump.md#-prerelease_offset -[retry_after_failure]: commands/commit.md#retry -[allow_abort]: commands/check.md#allow-abort -[version-scheme]: commands/bump.md#version-scheme -[pre_bump_hooks]: commands/bump.md#pre_bump_hooks -[post_bump_hooks]: commands/bump.md#post_bump_hooks -[allowed_prefixes]: commands/check.md#allowed-prefixes -[additional-features]: https://github.com/tmbo/questionary#additional-features -[customization]: customization.md -[shortcuts]: customization.md#shortcut-keys -[template-customization]: customization.md#customizing-the-changelog-template -[annotated-tags-vs-lightweight]: https://stackoverflow.com/a/11514139/2047185 -[encoding]: tutorials/writing_commits.md#writing-commits diff --git a/docs/config/bump.md b/docs/config/bump.md new file mode 100644 index 0000000000..10ca5bcf8d --- /dev/null +++ b/docs/config/bump.md @@ -0,0 +1,217 @@ +# Bump Options + +<!-- When adding a new option, please keep the alphabetical order. --> + +## `annotated_tag` + +When set to `true`, `cz bump` is equivalent to `cz bump --annotated-tag`. + +```toml title="pyproject.toml" +[tool.commitizen] +annotated_tag = true +``` + +## `bump_message` + +Template used to specify the commit message generated when bumping. + +Defaults to: `bump: version $current_version → $new_version` + +| Variable | Description | +| ------------------ | ----------------------------------- | +| `$current_version` | the version existing before bumping | +| `$new_version` | version generated after bumping | + +```toml title="pyproject.toml" +[tool.commitizen] +bump_message = "release $current_version → $new_version [skip-ci]" +``` + +## `gpg_sign` + +When set to `true`, `cz bump` is equivalent to `cz bump --gpg-sign`. See [`--gpg-sign`](../commands/bump.md#-gpg-sign). + +```toml title="pyproject.toml" +[tool.commitizen] +gpg_sign = true +``` + +## `ignored_tag_formats` + +- Type: `list` +- Default: `[]` + +Tags matching those formats will be totally ignored and won't raise a warning. +Each entry uses the syntax as [`tag_format`](#tag_format) with the addition of `*` that will match everything (non-greedy). + +## `major_version_zero` + +When set to `true`, `cz bump` is equivalent to `cz bump --major-version-zero`. See [`--major-version-zero`](../commands/bump.md#-major-version-zero). + +```toml title="pyproject.toml" +[tool.commitizen] +major_version_zero = true +``` + +## `legacy_tag_formats` + +- Type: `list` +- Default: `[]` + +Legacy git tag formats, useful for old projects that changed tag format. +Tags matching those formats will be recognized as version tags and be included in the changelog. +Each entry uses the syntax as `tag_format`. + +## `pre_bump_hooks` + +A list of optional commands that will run right *after* updating [`version_files`](#version_files) and *before* actual committing and tagging the release. + +Useful when you need to generate documentation based on the new version. During +execution of the script, some environment variables are available: + +| Variable | Description | +| ---------------------------- | ---------------------------------------------------------- | +| `CZ_PRE_IS_INITIAL` | `True` when this is the initial release, `False` otherwise | +| `CZ_PRE_CURRENT_VERSION` | Current version, before the bump | +| `CZ_PRE_CURRENT_TAG_VERSION` | Current version tag, before the bump | +| `CZ_PRE_NEW_VERSION` | New version, after the bump | +| `CZ_PRE_NEW_TAG_VERSION` | New version tag, after the bump | +| `CZ_PRE_MESSAGE` | Commit message of the bump | +| `CZ_PRE_INCREMENT` | Whether this is a `MAJOR`, `MINOR` or `PATCH` release | +| `CZ_PRE_CHANGELOG_FILE_NAME` | Path to the changelog file, if available | + +```toml title="pyproject.toml" +[tool.commitizen] +pre_bump_hooks = [ + "scripts/generate_documentation.sh" +] +``` + +## `post_bump_hooks` + +A list of optional commands that will run right *after* committing and tagging the release. + +Useful when you need to send notifications about a release, or further automate deploying the +release. During execution of the script, some environment variables are available: + +| Variable | Description | +| ------------------------------ | ----------------------------------------------------------- | +| `CZ_POST_WAS_INITIAL` | `True` when this was the initial release, `False` otherwise | +| `CZ_POST_PREVIOUS_VERSION` | Previous version, before the bump | +| `CZ_POST_PREVIOUS_TAG_VERSION` | Previous version tag, before the bump | +| `CZ_POST_CURRENT_VERSION` | Current version, after the bump | +| `CZ_POST_CURRENT_TAG_VERSION` | Current version tag, after the bump | +| `CZ_POST_MESSAGE` | Commit message of the bump | +| `CZ_POST_INCREMENT` | Whether this was a `MAJOR`, `MINOR` or `PATCH` release | +| `CZ_POST_CHANGELOG_FILE_NAME` | Path to the changelog file, if available | + +```toml title="pyproject.toml" +[tool.commitizen] +post_bump_hooks = [ + "scripts/slack_notification.sh" +] +``` + +## `prerelease_offset` + +Offset with which to start counting prereleases. + +If not specified, defaults to `0`. + +```toml title="pyproject.toml" +[tool.commitizen] +prerelease_offset = 1 +``` + +!!! note + Under some circumstances, a prerelease cannot start with `0`-for example, in embedded projects where individual characters are encoded as bytes. You can specify an offset from which to start counting. + +## `tag_format` + +See [`--tag-format`](../commands/bump.md#-tag-format). + +## `update_changelog_on_bump` + +When set to `true`, `cz bump` is equivalent to `cz bump --changelog`. + +```toml title="pyproject.toml" +[tool.commitizen] +update_changelog_on_bump = true +``` + +## `version_files` + +Identify the files or glob patterns which should be updated with the new version. + +Commitizen will update its configuration file automatically when bumping, +regardless of whether the file is present or not in `version_files`. + +You may specify the `version_files` in your configuration file. + +```toml title="pyproject.toml" +[tool.commitizen] +version_files = [ + "src/__version__.py", +] +``` + +It is also possible to provide a pattern for each file, separated by a colon (e.g. `file:pattern`). See the below example for more details. + +```toml title="pyproject.toml" +[tool.commitizen] +version_files = [ + "packages/*/pyproject.toml:version", + "setup.json:version", +] +``` + +!!! note "Example scenario" + We have a project with the following configuration file `pyproject.toml`: + + ```toml title="pyproject.toml" + [tool.commitizen] + version_files = [ + "src/__version__.py", + "packages/*/pyproject.toml:version", + "setup.json:version", + ] + ``` + + For the reference `"setup.json:version"`, it means that it will look for a file `setup.json` and will only change the lines that contain the substring `"version"`. + + For example, if the content of `setup.json` is: + + <!-- DEPENDENCY: repeated_version_number.json --> + + ```json title="setup.json" + { + "name": "magictool", + "version": "1.2.3", + "dependencies": { + "lodash": "1.2.3" + } + } + ``` + + After running `cz bump 2.0.0`, its content will be updated to: + + ```diff title="setup.json" + { + "name": "magictool", + - "version": "1.2.3", + + "version": "2.0.0", + "dependencies": { + "lodash": "1.2.3" + } + } + ``` + +!!! note + Files can be specified using relative (to the execution) paths, absolute paths, or glob patterns. + +!!! note "Historical note" + This option was renamed from `files` to `version_files`. + +## `version_scheme` + +See [`--version-scheme`](../commands/bump.md#-version-scheme). diff --git a/docs/config/changelog.md b/docs/config/changelog.md new file mode 100644 index 0000000000..86d4363105 --- /dev/null +++ b/docs/config/changelog.md @@ -0,0 +1,8 @@ +# Changelog Options + +<!-- When adding a new option, please keep the alphabetical order. --> +<!-- If there is a new configuration option that doesn't have a corresponding command line option, please add it here. --> + +As for now, each of the options that is used by `cz changelog` command can correlate to a command line option. + +See [changelog command line options](../commands/changelog.md#command-line-options) for more details. diff --git a/docs/config/check.md b/docs/config/check.md new file mode 100644 index 0000000000..2c8dda27b4 --- /dev/null +++ b/docs/config/check.md @@ -0,0 +1,27 @@ +# Check Options + +<!-- When adding a new option, please keep the alphabetical order. --> + +## `allow_abort` + +- Type: `bool` +- Default: `False` + +Disallow empty commit messages. Useful in CI. + +## `allowed_prefixes` + +- Type: `list` +- Default: `["Merge", "Revert", "Pull request", "fixup!", "squash!"]` + +List of prefixes that commitizen ignores when verifying messages. + +## `message_length_limit` + +- Type: `int` +- Default: `0` (no limit) + +Maximum length of the commit message. Setting it to `0` disables the length limit. + +!!! note + This option can be overridden by the `-l/--message-length-limit` command line argument. diff --git a/docs/config/commit.md b/docs/config/commit.md new file mode 100644 index 0000000000..72fdff947c --- /dev/null +++ b/docs/config/commit.md @@ -0,0 +1,26 @@ +# Commit Options + +<!-- When adding a new option, please keep the alphabetical order. --> + +## `breaking_change_exclamation_in_title` + +- Type: `bool` +- Default: `False` + +When true, breaking changes will be also indicated by an exclamation mark in the commit title (e.g., `feat!: breaking change`). + +When false, breaking changes will be only indicated by `BREAKING CHANGE:` in the footer. See [writing commits](../tutorials/writing_commits.md) for more details. + +## `encoding` + +- Type: `str` +- Default: `"utf-8"` + +Sets the character encoding to be used when parsing commit messages. + +## `retry_after_failure` + +- Type: `bool` +- Default: `False` + +Retries failed commit when running `cz commit`. diff --git a/docs/config/configuration_file.md b/docs/config/configuration_file.md new file mode 100644 index 0000000000..8469102550 --- /dev/null +++ b/docs/config/configuration_file.md @@ -0,0 +1,234 @@ +# Configuration File + +Commitizen uses configuration files to customize its behavior for your project. These files define settings such as which commit rules to use, version management preferences, changelog generation options, and more. + +## Creating a Configuration File + +It is recommended to create a configuration file via our [`cz init`](../commands/init.md) command. This command will guide you through setting up your configuration file with the appropriate settings for your project. + +## File Location and Search Order + +Configuration files are typically located in the root of your project directory. Commitizen searches for configuration files in the following order: + +1. `pyproject.toml` (in the `[tool.commitizen]` section) +2. `.cz.toml` +3. `.cz.json` +4. `cz.json` +5. `.cz.yaml` +6. `cz.yaml` +7. `cz.toml` + +The first valid configuration file found will be used. If no configuration file is found, Commitizen will use its default settings. + +!!! tip + For Python projects, it's recommended to add your Commitizen configuration to `pyproject.toml` to keep all project configuration in one place. + +## Supported Formats + +Commitizen supports three configuration file formats: + +- **TOML** (`.toml`) - Recommended for Python projects +- **JSON** (`.json`) +- **YAML** (`.yaml`) + +All formats support the same configuration options. Choose the format that best fits your project's ecosystem. + +## Configuration Structure + +### TOML Format + +For TOML files, Commitizen settings are placed under the `[tool.commitizen]` section. If you're using a standalone `.cz.toml` or `cz.toml` file, you can use `[tool.commitizen]` or just `[commitizen]`. + +**Example: `pyproject.toml`, `.cz.toml` or `cz.toml`** + +```toml title="pyproject.toml" +[tool.commitizen] +name = "cz_conventional_commits" +version = "0.1.0" +version_provider = "commitizen" +version_scheme = "pep440" +version_files = [ + "src/__version__.py", + "pyproject.toml:version" +] +tag_format = "$version" +update_changelog_on_bump = true +changelog_file = "CHANGELOG.md" +changelog_incremental = false +bump_message = "bump: version $current_version → $new_version" +gpg_sign = false +annotated_tag = false +major_version_zero = false +prerelease_offset = 0 +retry_after_failure = false +allow_abort = false +message_length_limit = 0 +allowed_prefixes = [ + "Merge", + "Revert", + "Pull request", + "fixup!", + "squash!", + "amend!" +] +breaking_change_exclamation_in_title = false +use_shortcuts = false +pre_bump_hooks = [] +post_bump_hooks = [] +encoding = "utf-8" + +# Optional: Custom styling for prompts +style = [ + ["qmark", "fg:#ff9d00 bold"], + ["question", "bold"], + ["answer", "fg:#ff9d00 bold"], + ["pointer", "fg:#ff9d00 bold"], + ["highlighted", "fg:#ff9d00 bold"], + ["selected", "fg:#cc5454"], + ["separator", "fg:#cc5454"], + ["instruction", ""], + ["text", ""], + ["disabled", "fg:#858585 italic"] +] +``` + +### JSON Format + +For JSON files, Commitizen settings are placed under the `commitizen` key. + +**Example: `.cz.json` or `cz.json`** + +```json title=".cz.json" +{ + "commitizen": { + "name": "cz_conventional_commits", + "version": "0.1.0", + "version_provider": "commitizen", + "version_scheme": "pep440", + "version_files": [ + "src/__version__.py", + "pyproject.toml:version" + ], + "tag_format": "$version", + "update_changelog_on_bump": true, + "changelog_file": "CHANGELOG.md", + "changelog_incremental": false, + "bump_message": "bump: version $current_version → $new_version", + "gpg_sign": false, + "annotated_tag": false, + "major_version_zero": false, + "prerelease_offset": 0, + "retry_after_failure": false, + "allow_abort": false, + "message_length_limit": 0, + "allowed_prefixes": [ + "Merge", + "Revert", + "Pull request", + "fixup!", + "squash!", + "amend!" + ], + "breaking_change_exclamation_in_title": false, + "use_shortcuts": false, + "pre_bump_hooks": [], + "post_bump_hooks": [], + "encoding": "utf-8", + "style": [ + ["qmark", "fg:#ff9d00 bold"], + ["question", "bold"], + ["answer", "fg:#ff9d00 bold"], + ["pointer", "fg:#ff9d00 bold"], + ["highlighted", "fg:#ff9d00 bold"], + ["selected", "fg:#cc5454"], + ["separator", "fg:#cc5454"], + ["instruction", ""], + ["text", ""], + ["disabled", "fg:#858585 italic"] + ] + } +} +``` + +### YAML Format + +For YAML files, Commitizen settings are placed under the `commitizen` key. + +**Example: `.cz.yaml` or `cz.yaml`** + +```yaml title=".cz.yaml" +commitizen: + name: cz_conventional_commits + version: "0.1.0" + version_provider: commitizen + version_scheme: pep440 + version_files: + - src/__version__.py + - pyproject.toml:version + tag_format: "$version" + update_changelog_on_bump: true + changelog_file: CHANGELOG.md + changelog_incremental: false + bump_message: "bump: version $current_version → $new_version" + gpg_sign: false + annotated_tag: false + major_version_zero: false + prerelease_offset: 0 + retry_after_failure: false + allow_abort: false + message_length_limit: 0 + allowed_prefixes: + - Merge + - Revert + - Pull request + - fixup! + - squash! + - amend! + breaking_change_exclamation_in_title: false + use_shortcuts: false + pre_bump_hooks: [] + post_bump_hooks: [] + encoding: utf-8 + style: + - - qmark + - fg:#ff9d00 bold + - - question + - bold + - - answer + - fg:#ff9d00 bold + - - pointer + - fg:#ff9d00 bold + - - highlighted + - fg:#ff9d00 bold + - - selected + - fg:#cc5454 + - - separator + - fg:#cc5454 + - - instruction + - "" + - - text + - "" + - - disabled + - fg:#858585 italic +``` + +## Configuration Options + +For a complete list of all available configuration options and their descriptions, see the [Configuration Settings](../config/option.md) documentation. + +Key configuration categories include: + +- **Commit Rules**: `name` - Select which commit convention to use +- **Version Management**: `version`, `version_provider`, `version_scheme`, `version_files` +- **Tagging**: `tag_format`, `legacy_tag_formats`, `ignored_tag_formats`, `gpg_sign`, `annotated_tag` +- **Changelog**: `changelog_file`, `changelog_format`, `changelog_incremental`, `update_changelog_on_bump` +- **Bumping**: `bump_message`, `major_version_zero`, `prerelease_offset`, `pre_bump_hooks`, `post_bump_hooks` +- **Commit Validation**: `allowed_prefixes`, `message_length_limit`, `allow_abort`, `retry_after_failure` +- **Customization**: `customize`, `style`, `use_shortcuts`, `template`, `extras` + +## Customization + +For advanced customization, including creating custom commit rules, see the [Customization](../customization/config_file.md) documentation. + +!!! note + The `customize` option is only supported when using TOML configuration files. diff --git a/docs/config/option.md b/docs/config/option.md new file mode 100644 index 0000000000..5b7ce807b3 --- /dev/null +++ b/docs/config/option.md @@ -0,0 +1,38 @@ +# Misc Options + +## `name` + +- Type: `str` +- Default: `"cz_conventional_commits"` + +Name of the committing rules to use. + +## `version` + +- Type: `str` +- Default: `None` + +Current version. Example: `"0.1.2"`. Required if you use `version_provider = "commitizen"`. + +## `style` + +- Type: `list` +- Default: `[]` + +Style for the prompts (It will merge this value with default style.) See [Styling your prompts with your favorite colors](https://github.com/tmbo/questionary#additional-features) for more details. + +## `customize` + +- Type: `dict` +- Default: `None` + +**This is only supported when config through `toml` configuration file.** + +Custom rules for committing and bumping. See [customization](../customization/config_file.md) for more details. + +## `use_shortcuts` + +- Type: `bool` +- Default: `False` + +Show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. See [shortcut keys](../customization/config_file.md#shortcut-keys) for more details. diff --git a/docs/config/version_provider.md b/docs/config/version_provider.md new file mode 100644 index 0000000000..859f84f781 --- /dev/null +++ b/docs/config/version_provider.md @@ -0,0 +1,326 @@ +# Version Providers + +Version providers are the mechanism by which Commitizen reads and writes version information in your project. + +They abstract away the details of where and how version numbers are stored, allowing Commitizen to work seamlessly with different project types and package management systems. + +## Overview + +By default, Commitizen uses the `commitizen` provider, which stores the version in your Commitizen configuration file. +However, you can configure Commitizen to use any available provider that matches your project's setup. +This is particularly useful when you want Commitizen to manage versions in the same location as your package manager (e.g., `package.json` for Node.js projects, `pyproject.toml` for Python projects). + +## Built-in Providers + +Commitizen includes several built-in version providers for common package management formats: + +### `commitizen` (Default) + +The default version provider stores and retrieves the version from your Commitizen configuration file (e.g., `pyproject.toml`, `.cz.toml`, etc.). + +**Use when:** + +- You want to keep version management separate from your package manager +- Your project doesn't use a standard package manager +- You need maximum flexibility in version management + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "commitizen" +version = "0.1.0" # Required when using this provider +``` + +### `scm` + +Fetches the version from Git tags using `git describe`. This provider **only reads** version information and never writes it back to files. It's designed to work with tools like `setuptools-scm` or other package manager `*-scm` plugins that derive version numbers from Git history. + +**Use when:** + +- You're using `setuptools-scm` or similar tools +- You want version numbers derived from Git tags +- You don't want Commitizen to modify any files for version management + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "scm" +# No version field needed - it's read from Git tags +``` + +!!! note + The `scm` provider is read-only. When you run `cz bump`, it will create a Git tag but won't update any files. This is intentional and works well with tools that derive versions from Git tags. + +### `pep621` + +Manages version in `pyproject.toml` under the `project.version` field, following [PEP 621](https://peps.python.org/pep-0621/) standards. + +**Use when:** + +- You're using a modern Python project with PEP 621-compliant `pyproject.toml` +- You want version management integrated with your Python project metadata + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "pep621" +``` + +**Example `pyproject.toml`:** +```toml +[project] +name = "my-package" +version = "0.1.0" # Managed by Commitizen +``` + +### `poetry` + +Manages version in `pyproject.toml` under the `tool.poetry.version` field, which is used by the [Poetry](https://python-poetry.org/) package manager. This approach is recommended only for users running Poetry versions earlier than 2.0 or relying on Poetry-specific features. For most users on Poetry 2.0 or later, it is recommended to use `pep621` instead. [Read More](https://python-poetry.org/docs/main/managing-dependencies/) + +**Use when:** + +- You're using Poetry < 2.0 as your Python package manager +- You're using Poetry >= 2.0 as your Python package manager, but don't need poetry-specific features +- You want Commitizen to manage the version that Poetry uses + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "poetry" +``` + +**Example `pyproject.toml`:** +```toml +[tool.poetry] +name = "my-package" +version = "0.1.0" # Managed by Commitizen +``` + +### `uv` + +Manages version in both `pyproject.toml` (`project.version`) and `uv.lock` (`package.version` for the matching package name). This ensures consistency between your project metadata and lock file. + + +!!! note + Even though uv follows PEP 621 format, `pep621` does not manage the version in `uv.lock`. `uv` is still suggested for uv users. + +**Use when:** + +- You're using `uv` as your Python package manager +- You want version synchronization between `pyproject.toml` and `uv.lock` + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "uv" +``` + +### `cargo` + +Manages version in both `Cargo.toml` (`package.version`) and `Cargo.lock` (`package.version` for the matching package name). This ensures consistency between your Rust project's manifest and lock file. + +**Use when:** + +- You're working with a Rust project using Cargo +- You want Commitizen to manage Rust package versions + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "cargo" +``` + +**Example `Cargo.toml`:** +```toml +[package] +name = "my-crate" +version = "0.1.0" # Managed by Commitizen +``` + +### `npm` + +Manages version in `package.json` and optionally synchronizes with `package-lock.json` and `npm-shrinkwrap.json` if they exist. + +**Use when:** + +- You're working with a Node.js/JavaScript project +- You want Commitizen to manage npm package versions + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "npm" +``` + +**Example `package.json`:** +```json +{ + "name": "my-package", + "version": "0.1.0" +} +``` + +### `composer` + +Manages version in `composer.json` under the `version` field, used by PHP's Composer package manager. + +**Use when:** + +- You're working with a PHP project using Composer +- You want Commitizen to manage Composer package versions + +**Configuration:** +```toml +[tool.commitizen] +version_provider = "composer" +``` + +**Example `composer.json`:** +```json +{ + "name": "vendor/package", + "version": "0.1.0" +} +``` + +## Provider Comparison Table + +| Provider | File(s) Modified | Read-Only | Best For | +| ------------ | ----------------------------------- | --------- | --------------------------------- | +| `commitizen` | Commitizen config file | No | General use, flexible projects | +| `scm` | None (reads from Git tags) | Yes | `setuptools-scm` users | +| `pep621` | `pyproject.toml` (`project.version`) | No | Modern Python (PEP 621) | +| `poetry` | `pyproject.toml` (`tool.poetry.version`) | No | Poetry projects | +| `uv` | `pyproject.toml` + `uv.lock` | No | uv package manager | +| `cargo` | `Cargo.toml` + `Cargo.lock` | No | Rust/Cargo projects | +| `npm` | `package.json` (+ lock files) | No | Node.js/npm projects | +| `composer` | `composer.json` | No | PHP/Composer projects | + +## Creating Custom Version Providers + +If none of the built-in providers meet your needs, you can create a custom version provider by extending the `VersionProvider` base class and registering it as a plugin. + +### Step 1: Create Your Provider Class + +Create a Python file (e.g., `my_provider.py`) that extends `VersionProvider`: + +```python title="my_provider.py" +from pathlib import Path +from commitizen.providers import VersionProvider + + +class MyProvider(VersionProvider): + """ + Custom version provider that reads/writes from a VERSION file. + """ + + def get_version(self) -> str: + """Read version from VERSION file.""" + version_file = Path("VERSION") + if not version_file.exists(): + return "0.0.0" + return version_file.read_text().strip() + + def set_version(self, version: str) -> None: + """Write version to VERSION file.""" + version_file = Path("VERSION") + version_file.write_text(f"{version}\n") +``` + +### Step 2: Register as an Entry Point + +Register your provider using the `commitizen.provider` entry point. You can do this in your `setup.py`, `setup.cfg`, or `pyproject.toml`: + +**Using `pyproject.toml` (recommended):** + +```toml title="pyproject.toml" +[project] +name = "my-commitizen-provider" +version = "0.1.0" +dependencies = ["commitizen"] + +[project.entry-points."commitizen.provider"] +my-provider = "my_provider:MyProvider" +``` + +**Using `setup.py` (for legacy setup):** + +```python title="setup.py" +from setuptools import setup + +setup( + name="my-commitizen-provider", + version="0.1.0", + py_modules=["my_provider"], + install_requires=["commitizen"], + entry_points={ + "commitizen.provider": [ + "my-provider = my_provider:MyProvider", + ] + }, +) +``` + +### Step 3: Install and Use + +1. Install your provider package: + ```bash + pip install -e . + ``` + +2. Configure Commitizen to use your provider: + ```toml + [tool.commitizen] + version_provider = "my-provider" + ``` + +### Provider Implementation Guidelines + +When creating a custom provider, keep these guidelines in mind: + +- **`get_version()`** should return a string representing the current version. If no version is found, you can return `"0.0.0"` or raise an appropriate exception. +- **`set_version(version: str)`** should write the version to your chosen storage location. The version string will be properly formatted according to your `version_scheme` setting. +- The provider has access to `self.config`, which is a `BaseConfig` instance containing all Commitizen settings. +- For file-based providers, consider using the `FileProvider` or `JsonProvider`/`TomlProvider` base classes from `commitizen.providers.base_provider` to simplify implementation. + +### Example: JSON-based Provider + +Here's a more complete example using the `JsonProvider` base class: + +```python title="json_version_provider.py" +from commitizen.providers.base_provider import JsonProvider + + +class JsonVersionProvider(JsonProvider): + """ + Version provider that uses a custom version.json file. + """ + + filename = "version.json" + + def get(self, document): + """Extract version from JSON document.""" + return document["version"] + + def set(self, document, version): + """Set version in JSON document.""" + document["version"] = version +``` + +This example leverages the `JsonProvider` base class, which handles file reading, writing, and JSON parsing automatically. + +## Choosing the Right Provider + +Select a version provider based on your project's characteristics: + +- **Python projects** + - **with `uv`**: Use `uv` + - **with `pyproject.toml` that follows PEP 621**: Use `pep621` + - **with Poetry**: Use `poetry` + - **setuptools-scm**: Use `scm` +- **Rust projects**: Use `cargo` +- **Node.js projects**: Use `npm` +- **PHP projects**: Use `composer` +- **Other cases or custom needs**: Use `commitizen` (default) or create a custom provider + +Remember that you can always use `version_files` in combination with any provider to update additional files during version bumps, regardless of which provider you choose for reading/writing the primary version. diff --git a/docs/contributing.md b/docs/contributing.md index 0da1707da6..741a13f213 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1,32 +1,83 @@ -## Contributing to commitizen - -First of all, thank you for taking the time to contribute! 🎉 - -When contributing to [commitizen](https://github.com/commitizen-tools/commitizen), please first create an [issue](https://github.com/commitizen-tools/commitizen/issues) to discuss the change you wish to make before making a change. - -If you're a first-time contributor, you can check the issues with [good first issue](https://github.com/commitizen-tools/commitizen/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tag. - -## Install before contributing - -1. Install [poetry](https://python-poetry.org/) `>=2.0.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 - -1. Fork [the repository](https://github.com/commitizen-tools/commitizen). -2. Clone the repository from your GitHub. -3. Setup development environment through [poetry](https://python-poetry.org/) (`poetry install`). -4. Setup [pre-commit](https://pre-commit.com/) hook (`poetry setup-pre-commit`) -5. Check out a new branch and add your modification. -6. Add test cases for all your changes. - (We use [CodeCov](https://codecov.io/) to ensure our test coverage does not drop.) -7. Use [commitizen](https://github.com/commitizen-tools/commitizen) to do git commit. We follow [conventional commits](https://www.conventionalcommits.org/). -8. Run `poetry all` to ensure you follow the coding style and the tests pass. -9. Optionally, update the `./docs/README.md` or `docs/images/cli_help` (through running `poetry doc:screenshots`). -9. **Do not** update the `CHANGELOG.md`, it will be automatically created after merging to `master`. -10. **Do not** update the versions in the project, they will be automatically updated. -10. If your changes are about documentation. Run `poetry doc` to serve documentation locally and check whether there is any warning or error. -11. Send a [pull request](https://github.com/commitizen-tools/commitizen/pulls) 🙏 +## Contributing to Commitizen + +First, thank you for taking the time to contribute! 🎉 Your contributions help make Commitizen better for everyone. + +When contributing to Commitizen, we encourage you to: + +1. First, check out the [issues](https://github.com/commitizen-tools/commitizen/issues) and [Features we won't add](features_wont_add.md) to see if there's already a discussion about the change you wish to make. +2. If there's no discussion, [create an issue](https://github.com/commitizen-tools/commitizen/issues/new) to discuss your proposed changes. +3. Follow our [development workflow](#development-workflow) and guidelines below. + +If you're a first-time contributor, please check out issues labeled [good first issue](https://github.com/commitizen-tools/commitizen/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) - these are specifically chosen to be beginner-friendly. + +## Prerequisites & Setup + +### Required Tools + +1. **Python Environment** + - Python `>=3.9` + - [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) `>=2.2.0` +2. **Version Control & Security** + - Git + - Commitizen + - [GPG](https://gnupg.org) for commit signing + - [Installation page](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation) + - For Mac users: `brew install gnupg` + - For Windows users: Download from [Gpg4win](https://www.gpg4win.org/) + - For Linux users: Use your distribution's package manager (e.g., `apt install gnupg` for Ubuntu) + +### Getting Started + +1. Fork [Commitizen](https://github.com/commitizen-tools/commitizen) +2. Clone your fork: + ```bash + git clone https://github.com/YOUR_USERNAME/commitizen.git + cd commitizen + ``` +3. Add the upstream repository: + ```bash + git remote add upstream https://github.com/commitizen-tools/commitizen.git + ``` +4. Set up the development environment (nix users go to [nix section](#nix)): + ```bash + poetry install + ``` +5. Set up pre-commit hooks: + ```bash + poetry setup-pre-commit + ``` + +## Development Workflow + +1. **Create a New Branch** + ```bash + git switch -c feature/your-feature-name + # or + git switch -c fix/your-bug-fix + ``` +2. **Make Your Changes** + - Write your code + - Add tests for new functionalities or fixes + - Update documentation if needed + - Follow the existing code style +3. **Testing** + - Run the full test suite: `poetry all` + - Ensure test coverage doesn't drop (we use [CodeCov](https://app.codecov.io/gh/commitizen-tools/commitizen)) + - For documentation changes, run `poetry doc` to check for warnings/errors +4. **Committing Changes** + - Use Commitizen to make commits (we follow [conventional commits](https://www.conventionalcommits.org/)) + - Example: `cz commit` +5. **Documentation** + - Update `docs/README.md` if needed + - For CLI help screenshots: `poetry doc:screenshots` + - Prefer [Google style documentation](https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings), which works well with editors like VSCode and PyCharm + - **DO NOT** update `CHANGELOG.md` (automatically generated) + - **DO NOT** update version numbers (automatically handled) +6. **Pull Request** + - Push your changes: `git push origin your-branch-name` + - Create a pull request on GitHub + - Ensure CI checks pass + - Wait for review and address any feedback ## Use of GitHub Labels @@ -45,8 +96,8 @@ If you're a first-time contributor, you can check the issues with [good first is * pr-status: wait-for-modification * pr-status: wait-for-response * pr-status: ready-to-merge -* needs: test-case *(pr only)* -* needs: documentation *(pr only)* +* needs: test-case *(PR only)* +* needs: documentation *(PR only)* * type: feature * type: bug * type: documentation @@ -57,7 +108,7 @@ If you're a first-time contributor, you can check the issues with [good first is * os: macOS -### Issue life cycle +## Issue life cycle ```mermaid graph TD @@ -75,7 +126,7 @@ graph TD close --> output[/close/] ``` -### Pull request life cycle +## Pull request life cycle ```mermaid flowchart TD @@ -104,5 +155,15 @@ flowchart TD review ``` +## Nix + +If you have installed poetry globally, the project won't work because it requires `poethepoet` for command management. -[conventional-commits]: https://www.conventionalcommits.org/ +You'll have to install poetry locally. + +```sh +python -m venv .venv +. .venv/bin/activate +pip install -U pip && pip install poetry +poetry install +``` diff --git a/docs/contributing_tldr.md b/docs/contributing_tldr.md new file mode 100644 index 0000000000..7b040c2951 --- /dev/null +++ b/docs/contributing_tldr.md @@ -0,0 +1,48 @@ +Feel free to send a PR to update this file if you find anything useful. 🙇 + +## Environment + +- Python `>=3.9` +- [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) `>=2.2.0` + +## Useful commands + +Please check the [pyproject.toml](https://github.com/commitizen-tools/commitizen/blob/master/pyproject.toml) for a comprehensive list of commands. + +### Code Changes + +```bash +# Ensure you have the correct dependencies, for nix user's see below +poetry install + +# Make ruff happy +poetry format + +# Check if ruff and mypy are happy +poetry lint + +# Check if mypy is happy in python 3.9 +mypy --python-version 3.9 + +# Run tests in parallel. +pytest -n auto # This may take a while. +pytest -n auto <test_suite> +``` + +### Documentation Changes + +```bash +# Build the documentation locally and check for broken links +poetry doc +``` + +### Nix Users + +If you are using Nix, you can install poetry locally by running: + +```sh +python -m venv .venv +. .venv/bin/activate +pip install -U pip && pip install poetry +poetry install +``` diff --git a/docs/customization.md b/docs/customization.md deleted file mode 100644 index 31749d1c83..0000000000 --- a/docs/customization.md +++ /dev/null @@ -1,530 +0,0 @@ -Customizing commitizen is not hard at all. -We have two different ways to do so. - -## 1. Customize in configuration file - -The basic steps are: - -1. Define your custom committing or bumping rules in the configuration file. -2. Declare `name = "cz_customize"` in your configuration file, or add `-n cz_customize` when running commitizen. - -Example: - -```toml -[tool.commitizen] -name = "cz_customize" - -[tool.commitizen.customize] -message_template = "{{change_type}}:{% if show_message %} {{message}}{% endif %}" -example = "feature: this feature enable customize through config file" -schema = "<type>: <body>" -schema_pattern = "(feature|bug fix):(\\s.*)" -bump_pattern = "^(break|new|fix|hotfix)" -bump_map = {"break" = "MAJOR", "new" = "MINOR", "fix" = "PATCH", "hotfix" = "PATCH"} -change_type_order = ["BREAKING CHANGE", "feat", "fix", "refactor", "perf"] -info_path = "cz_customize_info.txt" -info = """ -This is customized info -""" -commit_parser = "^(?P<change_type>feature|bug fix):\\s(?P<message>.*)?" -changelog_pattern = "^(feature|bug fix)?(!)?" -change_type_map = {"feature" = "Feat", "bug fix" = "Fix"} - -[[tool.commitizen.customize.questions]] -type = "list" -name = "change_type" -choices = [{value = "feature", name = "feature: A new feature."}, {value = "bug fix", name = "bug fix: A bug fix."}] -# choices = ["feature", "fix"] # short version -message = "Select the type of change you are committing" - -[[tool.commitizen.customize.questions]] -type = "input" -name = "message" -message = "Body." - -[[tool.commitizen.customize.questions]] -type = "confirm" -name = "show_message" -message = "Do you want to add body message in commit?" -``` - -The equivalent example for a json config file: - -```json -{ - "commitizen": { - "name": "cz_customize", - "customize": { - "message_template": "{{change_type}}:{% if show_message %} {{message}}{% endif %}", - "example": "feature: this feature enable customize through config file", - "schema": "<type>: <body>", - "schema_pattern": "(feature|bug fix):(\\s.*)", - "bump_pattern": "^(break|new|fix|hotfix)", - "bump_map": { - "break": "MAJOR", - "new": "MINOR", - "fix": "PATCH", - "hotfix": "PATCH" - }, - "change_type_order": ["BREAKING CHANGE", "feat", "fix", "refactor", "perf"], - "info_path": "cz_customize_info.txt", - "info": "This is customized info", - "commit_parser": "^(?P<change_type>feature|bug fix):\\s(?P<message>.*)?", - "changelog_pattern": "^(feature|bug fix)?(!)?", - "change_type_map": {"feature": "Feat", "bug fix": "Fix"}, - "questions": [ - { - "type": "list", - "name": "change_type", - "choices": [ - { - "value": "feature", - "name": "feature: A new feature." - }, - { - "value": "bug fix", - "name": "bug fix: A bug fix." - } - ], - "message": "Select the type of change you are committing" - }, - { - "type": "input", - "name": "message", - "message": "Body." - }, - { - "type": "confirm", - "name": "show_message", - "message": "Do you want to add body message in commit?" - } - ] - } - } -} -``` - -And the correspondent example for a yaml file: - -```yaml -commitizen: - name: cz_customize - customize: - message_template: "{{change_type}}:{% if show_message %} {{message}}{% endif %}" - example: 'feature: this feature enable customize through config file' - schema: "<type>: <body>" - schema_pattern: "(feature|bug fix):(\\s.*)" - bump_pattern: "^(break|new|fix|hotfix)" - commit_parser: "^(?P<change_type>feature|bug fix):\\s(?P<message>.*)?" - changelog_pattern: "^(feature|bug fix)?(!)?" - change_type_map: - feature: Feat - bug fix: Fix - bump_map: - break: MAJOR - new: MINOR - fix: PATCH - hotfix: PATCH - change_type_order: ["BREAKING CHANGE", "feat", "fix", "refactor", "perf"] - info_path: cz_customize_info.txt - info: This is customized info - questions: - - type: list - name: change_type - choices: - - value: feature - name: 'feature: A new feature.' - - value: bug fix - name: 'bug fix: A bug fix.' - message: Select the type of change you are committing - - type: input - name: message - message: 'Body.' - - type: confirm - name: show_message - message: 'Do you want to add body message in commit?' -``` - -### Customize configuration - -| Parameter | Type | Default | Description | -| ------------------- | ------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `questions` | `Questions` | `None` | Questions regarding the commit message. Detailed below. The type `Questions` is an alias to `Iterable[MutableMapping[str, Any]]` which is defined in `commitizen.defaults`. It expects a list of dictionaries. | -| `message_template` | `str` | `None` | The template for generating message from the given answers. `message_template` should either follow [Jinja2][jinja2] formatting specification, and all the variables in this template should be defined in `name` in `questions` | -| `example` | `str` | `""` | (OPTIONAL) Provide an example to help understand the style. Used by `cz example`. | -| `schema` | `str` | `""` | (OPTIONAL) Show the schema used. Used by `cz schema`. | -| `schema_pattern` | `str` | `""` | (OPTIONAL) The regular expression used to do commit message validation. Used by `cz check`. | -| `info_path` | `str` | `""` | (OPTIONAL) The path to the file that contains explanation of the commit rules. Used by `cz info`. If not provided `cz info`, will load `info` instead. | -| `info` | `str` | `""` | (OPTIONAL) Explanation of the commit rules. Used by `cz info`. | -| `bump_map` | `dict` | `None` | (OPTIONAL) Dictionary mapping the extracted information to a `SemVer` increment type (`MAJOR`, `MINOR`, `PATCH`) | -| `bump_pattern` | `str` | `None` | (OPTIONAL) Regex to extract information from commit (subject and body) | -| `change_type_order`| `str` | `None` | (OPTIONAL) List of strings used to order the Changelog. All other types will be sorted alphabetically. Default is `["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"]` | -| `commit_parser` | `str` | `None` | (OPTIONAL) Regex to extract information used in creating changelog. [See more][changelog-spec] | -| `changelog_pattern` | `str` | `None` | (OPTIONAL) Regex to understand which commits to include in the changelog | -| `change_type_map` | `dict` | `None` | (OPTIONAL) Dictionary mapping the type of the commit to a changelog entry | - -[jinja2]: https://jinja.palletsprojects.com/en/2.10.x/ -[changelog-spec]: https://commitizen-tools.github.io/commitizen/commands/changelog/ - -#### Detailed `questions` content - -| Parameter | Type | Default | Description | -| ----------- | ------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `type` | `str` | `None` | The type of questions. Valid types: `list`, `select`, `input` and etc. The `select` type provides an interactive searchable list interface. [See More][different-question-types] | -| `name` | `str` | `None` | The key for the value answered by user. It's used in `message_template` | -| `message` | `str` | `None` | Detail description for the question. | -| `choices` | `list` | `None` | (OPTIONAL) The choices when `type = list` or `type = select`. Either use a list of values or a list of dictionaries with `name` and `value` keys. Keyboard shortcuts can be defined via `key`. See examples above. | -| `default` | `Any` | `None` | (OPTIONAL) The default value for this question. | -| `filter` | `str` | `None` | (OPTIONAL) Validator for user's answer. **(Work in Progress)** | -| `multiline` | `bool` | `False` | (OPTIONAL) Enable multiline support when `type = input`. | -| `use_search_filter` | `bool` | `False` | (OPTIONAL) Enable search/filter functionality for list/select type questions. This allows users to type and filter through the choices. | -| `use_jk_keys` | `bool` | `True` | (OPTIONAL) Enable/disable j/k keys for navigation in list/select type questions. Set to false if you prefer arrow keys only. | - -[different-question-types]: https://github.com/tmbo/questionary#different-question-types - -#### Shortcut keys - -When the [`use_shortcuts`](config.md#settings) config option is enabled, commitizen can show and use keyboard shortcuts to select items from lists directly. -For example, when using the `cz_conventional_commits` commitizen template, shortcut keys are shown when selecting the commit type. Unless otherwise defined, keyboard shortcuts will be numbered automatically. -To specify keyboard shortcuts for your custom choices, provide the shortcut using the `key` parameter in dictionary form for each choice you would like to customize. - -## 2. Customize through customizing a class - -The basic steps are: - -1. Inheriting from `BaseCommitizen` -2. Give a name to your rules. -3. Create a python package using `setup.py`, `poetry`, etc -4. Expose the class as a `commitizen.plugin` entrypoint - -Check an [example][convcomms] on how to configure `BaseCommitizen`. - -You can also automate the steps above through [cookiecutter](https://cookiecutter.readthedocs.io/en/1.7.0/). - -```sh -cookiecutter gh:commitizen-tools/commitizen_cz_template -``` - -See [commitizen_cz_template](https://github.com/commitizen-tools/commitizen_cz_template) for details. - -Once you publish your rules, you can send us a PR to the [Third-party section](./third-party-commitizen.md). - -### Custom commit rules - -Create a Python module, for example `cz_jira.py`. - -Inherit from `BaseCommitizen`, and you must define `questions` and `message`. The others are optional. - -```python -from commitizen.cz.base import BaseCommitizen -from commitizen.defaults import Questions - - -class JiraCz(BaseCommitizen): - # Questions = Iterable[MutableMapping[str, Any]] - # It expects a list with dictionaries. - def questions(self) -> Questions: - """Questions regarding the commit message.""" - questions = [ - {"type": "input", "name": "title", "message": "Commit title"}, - {"type": "input", "name": "issue", "message": "Jira Issue number:"}, - ] - return questions - - def message(self, answers: dict) -> str: - """Generate the message with the given answers.""" - return "{0} (#{1})".format(answers["title"], answers["issue"]) - - def example(self) -> str: - """Provide an example to help understand the style (OPTIONAL) - - Used by `cz example`. - """ - return "Problem with user (#321)" - - def schema(self) -> str: - """Show the schema used (OPTIONAL) - - Used by `cz schema`. - """ - return "<title> (<issue>)" - - def info(self) -> str: - """Explanation of the commit rules. (OPTIONAL) - - Used by `cz info`. - """ - return "We use this because is useful" -``` - -The next file required is `setup.py` modified from flask version. - -```python -from setuptools import setup - -setup( - name="JiraCommitizen", - version="0.1.0", - py_modules=["cz_jira"], - license="MIT", - long_description="this is a long description", - install_requires=["commitizen"], - entry_points={"commitizen.plugin": ["cz_jira = cz_jira:JiraCz"]}, -) -``` - -So in the end, we would have - - . - ├── cz_jira.py - └── setup.py - -And that's it. You can install it without uploading to pypi by simply -doing `pip install .` - -If you feel like it should be part of this repo, create a PR. - -### Custom bump rules - -You need to define 2 parameters inside your custom `BaseCommitizen`. - -| Parameter | Type | Default | Description | -| -------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------- | -| `bump_pattern` | `str` | `None` | Regex to extract information from commit (subject and body) | -| `bump_map` | `dict` | `None` | Dictionary mapping the extracted information to a `SemVer` increment type (`MAJOR`, `MINOR`, `PATCH`) | - -Let's see an example. - -```python -from commitizen.cz.base import BaseCommitizen - - -class StrangeCommitizen(BaseCommitizen): - bump_pattern = r"^(break|new|fix|hotfix)" - bump_map = {"break": "MAJOR", "new": "MINOR", "fix": "PATCH", "hotfix": "PATCH"} -``` - -That's it, your commitizen now supports custom rules, and you can run. - -```bash -cz -n cz_strange bump -``` - -[convcomms]: https://github.com/commitizen-tools/commitizen/blob/master/commitizen/cz/conventional_commits/conventional_commits.py - -### Custom changelog generator - -The changelog generator should just work in a very basic manner without touching anything. -You can customize it of course, and this are the variables you need to add to your custom `BaseCommitizen`. - -| Parameter | Type | Required | Description | -| -------------------------------- | ------------------------------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `commit_parser` | `str` | NO | Regex which should provide the variables explained in the [changelog description][changelog-des] | -| `changelog_pattern` | `str` | NO | Regex to validate the commits, this is useful to skip commits that don't meet your ruling standards like a Merge. Usually the same as bump_pattern | -| `change_type_map` | `dict` | NO | Convert the title of the change type that will appear in the changelog, if a value is not found, the original will be provided | -| `changelog_message_builder_hook` | `method: (dict, git.GitCommit) -> dict | list | None` | NO | Customize with extra information your message output, like adding links, this function is executed per parsed commit. Each GitCommit contains the following attrs: `rev`, `title`, `body`, `author`, `author_email`. Returning a falsy value ignore the commit. | -| `changelog_hook` | `method: (full_changelog: str, partial_changelog: Optional[str]) -> str` | NO | Receives the whole and partial (if used incremental) changelog. Useful to send slack messages or notify a compliance department. Must return the full_changelog | -| `changelog_release_hook` | `method: (release: dict, tag: git.GitTag) -> dict` | NO | Receives each generated changelog release and its associated tag. Useful to enrich a releases before they are rendered. Must return the update release - -```python -from commitizen.cz.base import BaseCommitizen -import chat -import compliance - - -class StrangeCommitizen(BaseCommitizen): - changelog_pattern = r"^(break|new|fix|hotfix)" - commit_parser = r"^(?P<change_type>feat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?:\s(?P<message>.*)?" - change_type_map = { - "feat": "Features", - "fix": "Bug Fixes", - "refactor": "Code Refactor", - "perf": "Performance improvements", - } - - def changelog_message_builder_hook( - self, parsed_message: dict, commit: git.GitCommit - ) -> dict | list | None: - rev = commit.rev - m = parsed_message["message"] - parsed_message[ - "message" - ] = f"{m} {rev} [{commit.author}]({commit.author_email})" - return parsed_message - - def changelog_release_hook(self, release: dict, tag: git.GitTag) -> dict: - release["author"] = tag.author - return release - - def changelog_hook( - self, full_changelog: str, partial_changelog: Optional[str] - ) -> str: - """Executed at the end of the changelog generation - - full_changelog: it's the output about to being written into the file - partial_changelog: it's the new stuff, this is useful to send slack messages or - similar - - Return: - the new updated full_changelog - """ - if partial_changelog: - chat.room("#committers").notify(partial_changelog) - if full_changelog: - compliance.send(full_changelog) - full_changelog.replace(" fix ", " **fix** ") - return full_changelog -``` - -[changelog-des]: ./commands/changelog.md#description - -### Raise Customize Exception - -If you want `commitizen` to catch your exception and print the message, you'll have to inherit `CzException`. - -```python -from commitizen.cz.exception import CzException - - -class NoSubjectProvidedException(CzException): - ... -``` - -### Migrating from legacy plugin format - -Commitizen migrated to a new plugin format relying on `importlib.metadata.EntryPoint`. -Migration should be straight-forward for legacy plugins: - -- Remove the `discover_this` line from you plugin module -- Expose the plugin class under as a `commitizen.plugin` entrypoint. - -The name of the plugin is now determined by the name of the entrypoint. - -#### Example - -If you were having a `CzPlugin` class in a `cz_plugin.py` module like this: - -```python -from commitizen.cz.base import BaseCommitizen - - -class PluginCz(BaseCommitizen): - ... - - -discover_this = PluginCz -``` - -Then remove the `discover_this` line: - -```python -from commitizen.cz.base import BaseCommitizen - - -class PluginCz(BaseCommitizen): - ... -``` - -and expose the class as entrypoint in you setuptools: - -```python -from setuptools import setup - -setup( - name="MyPlugin", - version="0.1.0", - py_modules=["cz_plugin"], - entry_points={"commitizen.plugin": ["plugin = cz_plugin:PluginCz"]}, - ..., -) -``` - -Then your plugin will be available under the name `plugin`. - -## Customizing the changelog template - -Commitizen gives you the possibility to provide your own changelog template, by: - -- providing one with your customization class -- providing one from the current working directory and setting it: - - as [configuration][template-config] - - as `--template` parameter to both `bump` and `changelog` commands -- either by providing a template with the same name as the default template - -By default, the template used is the `CHANGELOG.md.j2` file from the commitizen repository. - -### Providing a template with your customization class - -There is 3 parameters available to change the template rendering from your custom `BaseCommitizen`. - -| Parameter | Type | Default | Description | -| ----------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------- | -| `template` | `str` | `None` | Provide your own template name (default to `CHANGELOG.md.j2`) | -| `template_loader` | `str` | `None` | Override the default template loader (so you can provide template from you customization class) | -| `template_extras` | `dict` | `None` | Provide some extra template parameters | - -Let's see an example. - -```python -from commitizen.cz.base import BaseCommitizen -from jinja2 import PackageLoader - - -class MyPlugin(BaseCommitizen): - template = "CHANGELOG.md.jinja" - template_loader = PackageLoader("my_plugin", "templates") - template_extras = {"key": "value"} -``` - -This snippet will: - -- use `CHANGELOG.md.jinja` as template name -- search for it in the `templates` directory for `my_plugin` package -- add the `key=value` variable in the template - -### Providing a template from the current working directory - -Users can provides their own template from their current working directory (your project root) by: - -- providing a template with the same name (`CHANGELOG.md.j2` unless overridden by your custom class) -- setting your template path as `template` configuration -- giving your template path as `--template` parameter to `bump` and `changelog` commands - -!!! Note - The path is relative to the current working directory, aka. your project root most of the time. - -### Template variables - -The default template use a single `tree` variable which is a list of entries (a release) with the following format: - -| Name | Type | Description | -| ---- | ---- | ----------- | -| version | `str` | The release version | -| date | `datetime` | The release date | -| changes | `list[tuple[str, list[Change]]]` | The release sorted changes list in the form `(type, changes)` | - -Each `Change` has the following fields: - -| Name | Type | Description | -| ---- | ---- | ----------- | -| scope | `str | None` | An optional scope | -| message | `str` | The commit message body | -| sha1 | `str` | The commit `sha1` | -| parents | `list[str]` | The parent commit(s) `sha1`(s) | -| author | `str` | The commit author name | -| author_email | `str` | The commit author email | - -!!! Note - The field values depend on the customization class and/or the settings you provide - -The `parents` field can be used to identify merge commits and generate a changelog based on those. Another use case -is listing commits that belong to the same pull request. - -When using another template (either provided by a plugin or by yourself), you can also pass extra template variables -by: - -- defining them in your configuration with the [`extras` settings][extras-config] -- providing them on the commandline with the `--extra/-e` parameter to `bump` and `changelog` commands - -[template-config]: config.md#template -[extras-config]: config.md#extras diff --git a/docs/customization/changelog_template.md b/docs/customization/changelog_template.md new file mode 100644 index 0000000000..60f40ebea7 --- /dev/null +++ b/docs/customization/changelog_template.md @@ -0,0 +1,85 @@ + +# Customizing the changelog template + +Commitizen gives you the possibility to provide your own changelog template, by: + +- providing one with your customization class +- providing one from the current working directory and setting it: + - Through the configuration file + - as `--template` parameter to both `bump` and `changelog` commands +- either by providing a template with the same name as the default template + +By default, the template used is the `CHANGELOG.md.j2` file from the Commitizen repository. + +## Providing a template with your customization class + +There are 3 parameters available to change the template rendering from your custom `BaseCommitizen`. + +| Parameter | Type | Default | Description | +| ----------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------- | +| `template` | `str` | `None` | Provide your own template name (default to `CHANGELOG.md.j2`) | +| `template_loader` | `str` | `None` | Override the default template loader (so you can provide template from your customization class) | +| `template_extras` | `dict` | `None` | Provide some extra template parameters | + +Let's see an example. + +```python +from commitizen.cz.base import BaseCommitizen +from jinja2 import PackageLoader + + +class MyPlugin(BaseCommitizen): + template = "CHANGELOG.md.jinja" + template_loader = PackageLoader("my_plugin", "templates") + template_extras = {"key": "value"} +``` + +This snippet will: + +- use `CHANGELOG.md.jinja` as template name +- search for it in the `templates` directory for `my_plugin` package +- add the `key=value` variable in the template + +## Providing a template from the current working directory + +Users can provide their own template from their current working directory (your project root) by: + +- providing a template with the same name (`CHANGELOG.md.j2` unless overridden by your custom class) +- setting your template path as `template` configuration +- giving your template path as `--template` parameter to `bump` and `changelog` commands + +!!! note + The path is relative to the current working directory, aka your project root most of the time. + +## Template variables + +The default template use a single `tree` variable which is a list of entries (a release) with the following format: + +| Name | Type | Description | +| ---- | ---- | ----------- | +| version | `str` | The release version | +| date | `datetime` | The release date | +| changes | `list[tuple[str, list[Change]]]` | The release sorted changes list in the form `(type, changes)` | + +Each `Change` has the following fields: + +| Name | Type | Description | +| ---- | ---- | ----------- | +| scope | `str | None` | An optional scope | +| message | `str` | The commit message body | +| sha1 | `str` | The commit `sha1` | +| parents | `list[str]` | The parent commit(s) `sha1`(s) | +| author | `str` | The commit author name | +| author_email | `str` | The commit author email | + +!!! note + The field values depend on the customization class and/or the settings you provide + +The `parents` field can be used to identify merge commits and generate a changelog based on those. Another use case +is listing commits that belong to the same pull request. + +When using another template (either provided by a plugin or by yourself), you can also pass extra template variables +by: + +- defining them in your configuration with the `extras` settings +- providing them on the command line with the `--extra/-e` parameter to `bump` and `changelog` commands diff --git a/docs/customization/config_file.md b/docs/customization/config_file.md new file mode 100644 index 0000000000..a5440342bf --- /dev/null +++ b/docs/customization/config_file.md @@ -0,0 +1,187 @@ +# Customize in configuration file + +The basic steps are: + +1. Define your custom committing or bumping rules in the configuration file. +2. Declare `name = "cz_customize"` in your configuration file, or add `-n cz_customize` when running Commitizen. + +Example: + +```toml title="pyproject.toml" +[tool.commitizen] +name = "cz_customize" + +[tool.commitizen.customize] +message_template = "{{change_type}}:{% if show_message %} {{message}}{% endif %}" +example = "feature: this feature enable customize through config file" +schema = "<type>: <body>" +schema_pattern = "(feature|bug fix):(\\s.*)" +bump_pattern = "^(break|new|fix|hotfix)" +bump_map = {"break" = "MAJOR", "new" = "MINOR", "fix" = "PATCH", "hotfix" = "PATCH"} +change_type_order = ["BREAKING CHANGE", "feat", "fix", "refactor", "perf"] +info_path = "cz_customize_info.txt" +info = """ +This is customized info +""" +commit_parser = "^(?P<change_type>feature|bug fix):\\s(?P<message>.*)?" +changelog_pattern = "^(feature|bug fix)?(!)?" +change_type_map = {"feature" = "Feat", "bug fix" = "Fix"} + +[[tool.commitizen.customize.questions]] +type = "list" +name = "change_type" +choices = [{value = "feature", name = "feature: A new feature."}, {value = "bug fix", name = "bug fix: A bug fix."}] +# choices = ["feature", "fix"] # short version +message = "Select the type of change you are committing" + +[[tool.commitizen.customize.questions]] +type = "input" +name = "message" +message = "Body." + +[[tool.commitizen.customize.questions]] +type = "confirm" +name = "show_message" +message = "Do you want to add body message in commit?" +``` + +The equivalent example for a json config file: + +```json title=".cz.json" +{ + "commitizen": { + "name": "cz_customize", + "customize": { + "message_template": "{{change_type}}:{% if show_message %} {{message}}{% endif %}", + "example": "feature: this feature enable customize through config file", + "schema": "<type>: <body>", + "schema_pattern": "(feature|bug fix):(\\s.*)", + "bump_pattern": "^(break|new|fix|hotfix)", + "bump_map": { + "break": "MAJOR", + "new": "MINOR", + "fix": "PATCH", + "hotfix": "PATCH" + }, + "change_type_order": ["BREAKING CHANGE", "feat", "fix", "refactor", "perf"], + "info_path": "cz_customize_info.txt", + "info": "This is customized info", + "commit_parser": "^(?P<change_type>feature|bug fix):\\s(?P<message>.*)?", + "changelog_pattern": "^(feature|bug fix)?(!)?", + "change_type_map": {"feature": "Feat", "bug fix": "Fix"}, + "questions": [ + { + "type": "list", + "name": "change_type", + "choices": [ + { + "value": "feature", + "name": "feature: A new feature." + }, + { + "value": "bug fix", + "name": "bug fix: A bug fix." + } + ], + "message": "Select the type of change you are committing" + }, + { + "type": "input", + "name": "message", + "message": "Body." + }, + { + "type": "confirm", + "name": "show_message", + "message": "Do you want to add body message in commit?" + } + ] + } + } +} +``` + +And the correspondent example for a yaml file: + +```yaml title=".cz.yaml" +commitizen: + name: cz_customize + customize: + message_template: '{{change_type}}:{% if show_message %} {{message}}{% endif %}' + example: 'feature: this feature enable customize through config file' + schema: '<type>: <body>' + schema_pattern: '(feature|bug fix):(\\s.*)' + bump_pattern: '^(break|new|fix|hotfix)' + commit_parser: '^(?P<change_type>feature|bug fix):\\s(?P<message>.*)?' + changelog_pattern: '^(feature|bug fix)?(!)?' + change_type_map: + feature: Feat + bug fix: Fix + bump_map: + break: MAJOR + new: MINOR + fix: PATCH + hotfix: PATCH + change_type_order: ['BREAKING CHANGE', 'feat', 'fix', 'refactor', 'perf'] + info_path: cz_customize_info.txt + info: This is customized info + questions: + - type: list + name: change_type + choices: + - value: feature + name: 'feature: A new feature.' + - value: bug fix + name: 'bug fix: A bug fix.' + message: Select the type of change you are committing + - type: input + name: message + message: 'Body.' + - type: confirm + name: show_message + message: 'Do you want to add body message in commit?' +``` + +## Configuration File Options + +| Parameter | Type | Default | Description | +| ------------------- | ------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `questions` | `Questions` | `None` | Questions regarding the commit message. Detailed below. The type `Questions` is an alias to `Iterable[MutableMapping[str, Any]]` which is defined in `commitizen.defaults`. It expects a list of dictionaries. | +| `message_template` | `str` | `None` | The template for generating message from the given answers. `message_template` should either follow [Jinja2][jinja2] formatting specification, and all the variables in this template should be defined in `name` in `questions` | +| `example` | `str` | `""` | (OPTIONAL) Provide an example to help understand the style. Used by `cz example`. | +| `schema` | `str` | `""` | (OPTIONAL) Show the schema used. Used by `cz schema`. | +| `schema_pattern` | `str` | `""` | (OPTIONAL) The regular expression used to do commit message validation. Used by `cz check`. | +| `info_path` | `str` | `""` | (OPTIONAL) The path to the file that contains explanation of the commit rules. Used by `cz info`. If not provided `cz info`, will load `info` instead. | +| `info` | `str` | `""` | (OPTIONAL) Explanation of the commit rules. Used by `cz info`. | +| `bump_map` | `dict` | `None` | (OPTIONAL) Dictionary mapping the extracted information to a `SemVer` increment type (`MAJOR`, `MINOR`, `PATCH`) | +| `bump_pattern` | `str` | `None` | (OPTIONAL) Regex to extract information from commit (subject and body) | +| `change_type_order` | `str` | `None` | (OPTIONAL) List of strings used to order the Changelog. All other types will be sorted alphabetically. Default is `["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"]` | +| `commit_parser` | `str` | `None` | (OPTIONAL) Regex to extract information used in creating changelog. [See more][changelog-spec] | +| `changelog_pattern` | `str` | `None` | (OPTIONAL) Regex to understand which commits to include in the changelog | +| `change_type_map` | `dict` | `None` | (OPTIONAL) Dictionary mapping the type of the commit to a changelog entry | + +[jinja2]: https://jinja.palletsprojects.com/en/2.10.x/ +[changelog-spec]: https://commitizen-tools.github.io/commitizen/commands/changelog/ + +### Detailed `questions` content + +| Parameter | Type | Default | Description | +| ----------- | ------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `type` | `str` | `None` | The type of questions. Valid types: `list`, `select`, `input`, etc. The `select` type provides an interactive searchable list interface. [See More][different-question-types] | +| `name` | `str` | `None` | The key for the value answered by user. It's used in `message_template` | +| `message` | `str` | `None` | Detail description for the question. | +| `choices` | `list` | `None` | (OPTIONAL) The choices when `type = list` or `type = select`. Either use a list of values or a list of dictionaries with `name` and `value` keys. Keyboard shortcuts can be defined via `key`. See examples above. | +| `default` | `Any` | `None` | (OPTIONAL) The default value for this question. | +| `filter` | `str` | `None` | (OPTIONAL) Validator for user's answer. **(Work in Progress)** | +| `multiline` | `bool` | `False` | (OPTIONAL) Enable multiline support when `type = input`. | +| `use_search_filter` | `bool` | `False` | (OPTIONAL) Enable search/filter functionality for list/select type questions. This allows users to type and filter through the choices. | +| `use_jk_keys` | `bool` | `True` | (OPTIONAL) Enable/disable j/k keys for navigation in list/select type questions. Set to false if you prefer arrow keys only. | + +[different-question-types]: https://github.com/tmbo/questionary#different-question-types + +### Shortcut keys + +When the `use_shortcuts` config option is enabled, Commitizen can show and use keyboard shortcuts to select items from lists directly. +For example, when using the `cz_conventional_commits` Commitizen template, shortcut keys are shown when selecting the commit type. +Unless otherwise defined, keyboard shortcuts will be numbered automatically. +To specify keyboard shortcuts for your custom choices, provide the shortcut using the `key` parameter in dictionary form for each choice you would like to customize. diff --git a/docs/customization/python_class.md b/docs/customization/python_class.md new file mode 100644 index 0000000000..c698b03dee --- /dev/null +++ b/docs/customization/python_class.md @@ -0,0 +1,310 @@ +# Customizing through a python class + +The basic steps are: + +1. Inheriting from `BaseCommitizen`. +2. Give a name to your rules. +3. Create a Python package using proper [build backend](https://packaging.python.org/en/latest/glossary/#term-Build-Backend) +4. Expose the class as a `commitizen.plugin` entrypoint. + +Check an [example][convcomms] on how to configure `BaseCommitizen`. + +You can also automate the steps above through [cookiecutter](https://cookiecutter.readthedocs.io/en/1.7.0/). + +```sh +cookiecutter gh:commitizen-tools/commitizen_cz_template +``` + +See [commitizen_cz_template](https://github.com/commitizen-tools/commitizen_cz_template) for details. + +See [Third-party plugins](../third-party-plugins/about.md) for more details on how to create a third-party Commitizen plugin. + +## Custom commit rules + +Create a Python module, for example `cz_jira.py`. + +Inherit from `BaseCommitizen`, and you must define `questions` and `message`. The others are optional. + +```python title="cz_jira.py" +from commitizen.cz.base import BaseCommitizen +from commitizen.defaults import Questions + + +class JiraCz(BaseCommitizen): + # Questions = Iterable[MutableMapping[str, Any]] + # It expects a list with dictionaries. + def questions(self) -> Questions: + """Questions regarding the commit message.""" + questions = [ + {"type": "input", "name": "title", "message": "Commit title"}, + {"type": "input", "name": "issue", "message": "Jira Issue number:"}, + ] + return questions + + def message(self, answers: dict) -> str: + """Generate the message with the given answers.""" + return f"answers['title'] (#answers['issue'])" + + def example(self) -> str: + """Provide an example to help understand the style (OPTIONAL) + + Used by `cz example`. + """ + return "Problem with user (#321)" + + def schema(self) -> str: + """Show the schema used (OPTIONAL) + + Used by `cz schema`. + """ + return "<title> (<issue>)" + + def info(self) -> str: + """Explanation of the commit rules. (OPTIONAL) + + Used by `cz info`. + """ + return "We use this because is useful" +``` + +The next file required is `setup.py` modified from flask version. + +```python title="setup.py" +from setuptools import setup + +setup( + name="JiraCommitizen", + version="0.1.0", + py_modules=["cz_jira"], + license="MIT", + long_description="this is a long description", + install_requires=["commitizen"], + entry_points={"commitizen.plugin": ["cz_jira = cz_jira:JiraCz"]}, +) +``` + +So in the end, we would have + + . + ├── cz_jira.py + └── setup.py + +And that's it. You can install it without uploading to PyPI by simply +doing `pip install .` + +## Custom bump rules + +You need to define 2 parameters inside your custom `BaseCommitizen`. + +| Parameter | Type | Default | Description | +| -------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------- | +| `bump_pattern` | `str` | `None` | Regex to extract information from commit (subject and body) | +| `bump_map` | `dict` | `None` | Dictionary mapping the extracted information to a `SemVer` increment type (`MAJOR`, `MINOR`, `PATCH`) | + +Let's see an example. + +```python title="cz_strange.py" +from commitizen.cz.base import BaseCommitizen + + +class StrangeCommitizen(BaseCommitizen): + bump_pattern = r"^(break|new|fix|hotfix)" + bump_map = {"break": "MAJOR", "new": "MINOR", "fix": "PATCH", "hotfix": "PATCH"} +``` + +That's it, your Commitizen now supports custom rules, and you can run. + +```bash +cz -n cz_strange bump +``` + +[convcomms]: https://github.com/commitizen-tools/commitizen/blob/master/commitizen/cz/conventional_commits/conventional_commits.py + +### Custom commit validation and error message + +The commit message validation can be customized by overriding the `validate_commit_message` and `format_error_message` +methods from `BaseCommitizen`. This allows for a more detailed feedback to the user where the error originates from. + +```python +import re +from commitizen.cz.base import BaseCommitizen, ValidationResult +from commitizen import git + + +class CustomValidationCz(BaseCommitizen): + def validate_commit_message( + self, + *, + commit_msg: str, + pattern: str | None, + allow_abort: bool, + allowed_prefixes: list[str], + max_msg_length: int, + ) -> ValidationResult: + """Validate commit message against the pattern.""" + if not commit_msg: + return allow_abort, [] if allow_abort else [f"commit message is empty"] + if pattern is None: + return True, [] + if any(map(commit_msg.startswith, allowed_prefixes)): + return True, [] + if max_msg_length: + msg_len = len(commit_msg.partition("\n")[0].strip()) + if msg_len > max_msg_length: + return False, [ + f"commit message is too long. Max length is {max_msg_length}" + ] + pattern_match = re.match(pattern, commit_msg) + if pattern_match: + return True, [] + else: + # Perform additional validation of the commit message format + # and add custom error messages as needed + return False, ["commit message does not match the pattern"] + + def format_exception_message( + self, ill_formatted_commits: list[tuple[git.GitCommit, list]] + ) -> str: + """Format commit errors.""" + displayed_msgs_content = "\n".join( + ( + f'commit "{commit.rev}": "{commit.message}"' + f"errors:\n" + "\n".join((f"- {error}" for error in errors)) + ) + for commit, errors in ill_formatted_commits + ) + return ( + "commit validation: failed!\n" + "please enter a commit message in the commitizen format.\n" + f"{displayed_msgs_content}\n" + f"pattern: {self.schema_pattern()}" + ) +``` + +## Custom changelog generator + +The changelog generator should just work in a very basic manner without touching anything. +You can customize it of course, and the following variables are the ones you need to add to your custom `BaseCommitizen`. + +| Parameter | Type | Required | Description | +| -------------------------------- | ------------------------------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `commit_parser` | `str` | NO | Regex which should provide the variables explained in the [changelog description][changelog-des] | +| `changelog_pattern` | `str` | NO | Regex to validate the commits, this is useful to skip commits that don't meet your ruling standards like a Merge. Usually the same as bump_pattern | +| `change_type_map` | `dict` | NO | Convert the title of the change type that will appear in the changelog, if a value is not found, the original will be provided | +| `changelog_message_builder_hook` | `method: (dict, git.GitCommit) -> dict | list | None` | NO | Customize with extra information your message output, like adding links, this function is executed per parsed commit. Each GitCommit contains the following attrs: `rev`, `title`, `body`, `author`, `author_email`. Returning a falsy value ignore the commit. | +| `changelog_hook` | `method: (full_changelog: str, partial_changelog: Optional[str]) -> str` | NO | Receives the whole and partial (if used incremental) changelog. Useful to send slack messages or notify a compliance department. Must return the full_changelog | +| `changelog_release_hook` | `method: (release: dict, tag: git.GitTag) -> dict` | NO | Receives each generated changelog release and its associated tag. Useful to enrich releases before they are rendered. Must return the update release + +```python title="cz_strange.py" +from commitizen.cz.base import BaseCommitizen +import chat +import compliance + + +class StrangeCommitizen(BaseCommitizen): + changelog_pattern = r"^(break|new|fix|hotfix)" + commit_parser = r"^(?P<change_type>feat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?:\s(?P<message>.*)?" + change_type_map = { + "feat": "Features", + "fix": "Bug Fixes", + "refactor": "Code Refactor", + "perf": "Performance improvements", + } + + def changelog_message_builder_hook( + self, parsed_message: dict, commit: git.GitCommit + ) -> dict | list | None: + rev = commit.rev + m = parsed_message["message"] + parsed_message[ + "message" + ] = f"{m} {rev} [{commit.author}]({commit.author_email})" + return parsed_message + + def changelog_release_hook(self, release: dict, tag: git.GitTag) -> dict: + release["author"] = tag.author + return release + + def changelog_hook( + self, full_changelog: str, partial_changelog: Optional[str] + ) -> str: + """Executed at the end of the changelog generation + + full_changelog: it's the output about to being written into the file + partial_changelog: it's the new stuff, this is useful to send slack messages or + similar + + Return: + the new updated full_changelog + """ + if partial_changelog: + chat.room("#committers").notify(partial_changelog) + if full_changelog: + compliance.send(full_changelog) + full_changelog.replace(" fix ", " **fix** ") + return full_changelog +``` + +## Raise Customize Exception + +If you want `commitizen` to catch your exception and print the message, you'll have to inherit `CzException`. + +```python +from commitizen.cz.exception import CzException + + +class NoSubjectProvidedException(CzException): + ... +``` + +## Migrating from legacy plugin format + +Commitizen migrated to a new plugin format relying on `importlib.metadata.EntryPoint`. +Migration should be straight-forward for legacy plugins: + +- Remove the `discover_this` line from your plugin module +- Expose the plugin class under as a `commitizen.plugin` entrypoint. + +The name of the plugin is now determined by the name of the entrypoint. + +### Example + +If you were having a `CzPlugin` class in a `cz_plugin.py` module like this: + +```python +from commitizen.cz.base import BaseCommitizen + + +class PluginCz(BaseCommitizen): + ... + + +discover_this = PluginCz +``` + +Then remove the `discover_this` line: + +```python +from commitizen.cz.base import BaseCommitizen + + +class PluginCz(BaseCommitizen): + ... +``` + +and expose the class as entrypoint in your `setuptools`: + +```python +from setuptools import setup + +setup( + name="MyPlugin", + version="0.1.0", + py_modules=["cz_plugin"], + entry_points={"commitizen.plugin": ["plugin = cz_plugin:PluginCz"]}, + ..., +) +``` + +Then your plugin will be available under the name `plugin`. diff --git a/docs/exit_codes.md b/docs/exit_codes.md index af9cb83627..1a214e2832 100644 --- a/docs/exit_codes.md +++ b/docs/exit_codes.md @@ -1,39 +1,142 @@ # Exit Codes -Commitizen handles expected exceptions through `CommitizenException` and returns different exit codes for different situations. They could be useful if you want to ignore specific errors in your pipeline. +Commitizen handles expected exceptions through `CommitizenException` and returns different exit codes for different situations. This reference is useful when you need to ignore specific errors in your CI/CD pipeline or automation scripts. -These exit codes can be found in `commitizen/exceptions.py::ExitCode`. +All exit codes are defined in [commitizen/exceptions.py](https://github.com/commitizen-tools/commitizen/blob/master/commitizen/exceptions.py). + +## Exit Code Reference | Exception | Exit Code | Description | | --------------------------- | --------- | ----------------------------------------------------------------------------------------------------------- | -| ExpectedExit | 0 | Expected exit | -| DryRunExit | 0 | Exit due to passing `--dry-run` option | -| NoCommitizenFoundException | 1 | Using a cz (e.g., `cz_jira`) that cannot be found in your system | -| NotAGitProjectError | 2 | Not in a git project | -| NoCommitsFoundError | 3 | No commit found | -| NoVersionSpecifiedError | 4 | Version can not be found in configuration file | -| NoPatternMapError | 5 | bump / changelog pattern or map can not be found in configuration file | -| BumpCommitFailedError | 6 | Commit error when bumping version | -| BumpTagFailedError | 7 | Tag error when bumping version | -| NoAnswersError | 8 | No user response given | -| CommitError | 9 | git commit error | -| NoCommitBackupError | 10 | Commit back up file cannot be found | -| NothingToCommitError | 11 | Nothing in staging to be committed | -| CustomError | 12 | `CzException` raised | -| NoCommandFoundError | 13 | No command found when running commitizen cli (e.g., `cz --debug`) | -| InvalidCommitMessageError | 14 | The commit message does not pass `cz check` | -| MissingConfigError | 15 | Configuration missed for `cz_customize` | -| NoRevisionError | 16 | No revision found | -| CurrentVersionNotFoundError | 17 | current version cannot be found in _version_files_ | -| InvalidCommandArgumentError | 18 | The argument provide to command is invalid (e.g. `cz check -commit-msg-file filename --rev-range master..`) | -| InvalidConfigurationError | 19 | An error was found in the Commitizen Configuration, such as duplicates in `change_type_order` | -| NotAllowed | 20 | `--incremental` cannot be combined with a `rev_range` | -| NoneIncrementExit | 21 | The commits found are not eligible to be bumped | -| CharacterSetDecodeError | 22 | The character encoding of the command output could not be determined | -| GitCommandError | 23 | Unexpected failure while calling a git command | -| InvalidManualVersion | 24 | Manually provided version is invalid | -| InitFailedError | 25 | Failed to initialize pre-commit | -| RunHookError | 26 | An error occurred during a hook execution | -| VersionProviderUnknown | 27 | `version_provider` setting is set to an unknown version provider identifier | -| VersionSchemeUnknown | 28 | `version_scheme` setting is set to an unknown version scheme identifier | -| ChangelogFormatUnknown | 29 | `changelog_format` setting is set to an unknown version scheme identifier or could not be guessed | +| `ExpectedExit` | 0 | Expected exit | +| `DryRunExit` | 0 | Exit due to passing `--dry-run` option | +| `NoCommitizenFoundException` | 1 | Using a cz (e.g., `cz_jira`) that cannot be found in your system | +| `NotAGitProjectError` | 2 | Not in a git project | +| `NoCommitsFoundError` | 3 | No commits found | +| `NoVersionSpecifiedError` | 4 | Version is not specified in configuration file | +| `NoPatternMapError` | 5 | bump / changelog pattern or map can not be found in configuration file | +| `BumpCommitFailedError` | 6 | Commit failed when bumping version | +| `BumpTagFailedError` | 7 | Tag failed when bumping version | +| `NoAnswersError` | 8 | No user response given | +| `CommitError` | 9 | git commit error | +| `NoCommitBackupError` | 10 | Commit backup file is not found | +| `NothingToCommitError` | 11 | Nothing in staging to be committed | +| `CustomError` | 12 | `CzException` raised | +| `NoCommandFoundError` | 13 | No command found when running Commitizen cli (e.g., `cz --debug`) | +| `InvalidCommitMessageError` | 14 | The commit message does not pass `cz check` | +| `MissingConfigError` | 15 | Configuration is missing for `cz_customize` | +| `NoRevisionError` | 16 | No revision found | +| `CurrentVersionNotFoundError`| 17 | Current version cannot be found in `version_files` | +| `InvalidCommandArgumentError`| 18 | The argument provided to the command is invalid (e.g. `cz check -commit-msg-file filename --rev-range master..`) | +| `InvalidConfigurationError` | 19 | An error was found in the Commitizen Configuration, such as duplicates in `change_type_order` | +| `NotAllowed` | 20 | Invalid combination of command line / configuration file options | +| `NoneIncrementExit` | 21 | The commits found are not eligible to be bumped | +| `CharacterSetDecodeError` | 22 | The character encoding of the command output could not be determined | +| `GitCommandError` | 23 | Unexpected failure while calling a git command | +| `InvalidManualVersion` | 24 | Manually provided version is invalid | +| `InitFailedError` | 25 | Failed to initialize pre-commit | +| `RunHookError` | 26 | An error occurred during a hook execution | +| `VersionProviderUnknown` | 27 | Unknown `version_provider` | +| `VersionSchemeUnknown` | 28 | Unknown `version_scheme` | +| `ChangelogFormatUnknown` | 29 | Unknown `changelog_format` or cannot be determined by the file extension | +| `ConfigFileNotFound` | 30 | The configuration file is not found | +| `ConfigFileIsEmpty` | 31 | The configuration file is empty | +| `CommitMessageLengthLimitExceededError`| 32 | The commit message length exceeds the given limit. | + +## Ignoring Exit Codes + +In some scenarios, you may want Commitizen to continue execution even when certain errors occur. This is particularly useful in CI/CD pipelines where you want to handle specific errors gracefully. + +### Using `--no-raise` Flag + +The `--no-raise` (or `-nr`) flag allows you to specify exit codes that should not cause Commitizen to exit with an error. You can use either: + +- **Exit code numbers**: `21`, `3`, `4` +- **Exit code names**: `NO_INCREMENT`, `NO_COMMITS_FOUND`, `NO_VERSION_SPECIFIED` +- **Mixed format**: `21,NO_COMMITS_FOUND,4` + +Multiple exit codes can be specified as a comma-separated list. + +### Common Use Cases + +#### Ignoring No Increment Errors + +The most common use case is to ignore `NoneIncrementExit` (exit code 21) when running `cz bump`. This allows the command to succeed even when no commits are eligible for a version bump: + +```sh +cz -nr 21 bump +``` + +Or using the exit code name: + +```sh +cz -nr NO_INCREMENT bump +``` + +This is useful in CI pipelines where you want to run `cz bump` regularly, but don't want the pipeline to fail when there are no version-worthy commits. + +#### Ignoring Multiple Exit Codes + +You can ignore multiple exit codes at once: + +```sh +cz --no-raise 21,3,4 bump +``` + +This example ignores: + +- `21` (`NoneIncrementExit`) - No eligible commits for bump +- `3` (`NoCommitsFoundError`) - No commits found +- `4` (`NoVersionSpecifiedError`) - Version not specified + +### Finding the Exit Code + +If you encounter an error and want to ignore it, you can find the exit code in two ways: + +#### Method 1: Check the Exit Code After Running + +After running a Commitizen command that fails, check the exit code: + +```sh +cz bump +echo $? # Prints the exit code (e.g., 21) +``` + +Then use that exit code with `--no-raise`: + +```sh +cz -nr 21 bump +``` + +#### Method 2: Look Up the Exception + +1. Check the error message to identify the exception type +2. Find the corresponding exit code in the table above +3. Use that exit code with `--no-raise` + +For example, if you see `NoneIncrementExit` in the error, look it up in the table to find it's exit code 21, then use: + +```sh +cz -nr 21 bump +``` + +### Best Practices + +- **Document your usage**: If you use `--no-raise` in scripts or CI/CD, document why specific exit codes are ignored +- **Be specific**: Only ignore exit codes you understand and have a reason to ignore +- **Test thoroughly**: Ensure that ignoring certain exit codes doesn't mask real problems in your workflow +- **Use exit code names**: When possible, use exit code names (e.g., `NO_INCREMENT`) instead of numbers for better readability + +### Example: CI/CD Pipeline + +Here's an example of using `--no-raise` in a CI/CD pipeline: + +```yaml +# .github/workflows/release.yml +- name: Bump version + run: | + cz -nr NO_INCREMENT bump || true + # Continue even if no version bump is needed +``` + +This ensures the pipeline continues even when there are no commits eligible for a version bump. diff --git a/docs/external_links.md b/docs/external_links.md index 388bcc8dea..1b5efb05df 100644 --- a/docs/external_links.md +++ b/docs/external_links.md @@ -1,16 +1,17 @@ -> If you have written over commitizen, make a PR and add the link here 💪 +> If you have written over Commitizen, make a PR and add the link here 💪 ## Talks | Name | Speaker | Occasion | Language | Extra | -| ------------------------------------------------------------------------- | --------------- | ---------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- | -| commitizen-tools: What can we gain from crafting a git message convention | Wei Lee | Taipey.py 2020 June Meetup, Remote Python Pizza 2020 | English | [slides](https://speakerdeck.com/leew/commitizen-tools-what-can-we-gain-from-crafting-a-git-message-convention-at-taipey-dot-py) | -| Automating release cycles | Santiago Fraire | PyAmsterdam June 24, 2020, Online | English | [slides](https://woile.github.io/commitizen-presentation/) | -| [Automatizando Releases con Commitizen y Github Actions][automatizando] | Santiago Fraire | PyConAr 2020, Remote | Español | [slides](https://woile.github.io/automating-releases-github-actions-presentation/#/) | +| ------------------------------------------------------------------------- | --------------- | ---------------------------------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| [Atomic Commits: An Easy & Proven Way to Manage & Automate Release Process](https://www.youtube.com/watch?v=IxzN9ClXhs8) | Wei Lee | COSCUP 2023 | Taiwanese Mandarin | [slides](https://speakerdeck.com/leew/atomic-commits-an-easy-and-proven-way-to-manage-and-automate-release-process) | +| commitizen-tools: What can we gain from crafting a git message convention | Wei Lee | Taipei.py 2020 June Meetup, Remote Python Pizza 2020 | English | [slides](https://speakerdeck.com/leew/commitizen-tools-what-can-we-gain-from-crafting-a-git-message-convention-at-taipey-dot-py) | +| Automating release cycles | Santiago Fraire | PyAmsterdam June 24, 2020, Online | English | [slides](https://woile.github.io/commitizen-presentation/) | +| [Automatizando Releases con Commitizen y Github Actions][automatizando] | Santiago Fraire | PyConAr 2020, Remote | Español | [slides](https://woile.github.io/automating-releases-github-actions-presentation/#/) | ## Articles -- [Python Table Manners - Commitizen: 規格化 commit message](https://lee-w.github.io/posts/tech/2020/03/python-table-manners-commitizen/) (Written in Traditional Mandarin) +- [Python Table Manners - Commitizen: 規格化 commit message](https://blog.wei-lee.me/posts/tech/2020/03/python-table-manners-commitizen/) (Written in Traditional Mandarin) - [Automating semantic release with commitizen](https://woile.dev/posts/automating-semver-releases-with-commitizen/) (English) - [How to Write Better Git Commit Messages – A Step-By-Step Guide](https://www.freecodecamp.org/news/how-to-write-better-git-commit-messages/?utm_source=tldrnewsletter) (English) - [Continuous delivery made easy (in Python)](https://medium.com/dev-genius/continuous-delivery-made-easy-in-python-c085e9c82e69) diff --git a/docs/faq.md b/docs/faq.md index 29d9f40512..9de0c43e41 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,6 +1,8 @@ -## Support for PEP621 +This page contains frequently asked questions about Commitizen. -PEP621 establishes a `[project]` definition inside `pyproject.toml` +## Support for [`PEP621`](https://peps.python.org/pep-0621/) + +`PEP621` establishes a `[project]` definition inside `pyproject.toml` ```toml [project] @@ -8,7 +10,8 @@ name = "spam" version = "2.5.1" ``` -Commitizen provides a [`pep621` version provider](config.md#version-providers) to get and set version from this field. +Commitizen provides a [`PEP621` version provider](config/version_provider.md) to get and set version from this field. + You just need to set the proper `version_provider` setting: ```toml @@ -20,12 +23,14 @@ version = "2.5.1" version_provider = "pep621" ``` -## Why are `revert` and `chore` valid types in the check pattern of cz conventional_commits but not types we can select? +## Why are `revert` and `chore` valid types in the check pattern of `cz_conventional_commits` but not types we can select? -`revert` and `chore` are added to the "pattern" in `cz check` in order to prevent backward errors, but officially they are not part of conventional commits, we are using the latest [types from Angular](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type) (they used to but were removed). -However, you can create a customized `cz` with those extra types. (See [Customization](customization.md)). +`revert` and `chore` are added to the `pattern` in `cz check` in order to prevent backward errors, but officially they are not part of conventional commits, we are using the latest [types from Angular](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type) (they used to but were removed). +However, you can create a customized `cz` with those extra types. (See [Customization](customization/config_file.md)). -See more discussion in issue [#142](https://github.com/commitizen-tools/commitizen/issues/142) and [#36](https://github.com/commitizen-tools/commitizen/issues/36) +See more discussion in +- [issue #142](https://github.com/commitizen-tools/commitizen/issues/142) +- [issue #36](https://github.com/commitizen-tools/commitizen/issues/36) ## How to revert a bump? @@ -39,23 +44,25 @@ git reset --hard HEAD This will remove the last tag created, plus the commit containing the update to `.cz.toml` and the changelog generated for the version. -In case the commit was pushed to the server you can remove it by running +In case the commit was pushed to the server, you can remove it by running: ```sh git push --delete origin <created_tag> ``` -## Is this project affiliated with the Commitizen JS project? +## Is this project affiliated with the [Commitizen JS][cz-js] project? -It is not affiliated. +**It is not affiliated.** Both are used for similar purposes, parsing commits, generating changelog and version we presume. This one is written in python to make integration easier for python projects and the other serves the JS packages. -They differ a bit in design, not sure if cz-js does any of this, but these are some of the stuff you can do with this repo (python's commitizen): +<!-- TODO: Add more details about the differences between Commitizen and Commitizen JS --> -- create custom rules, version bumps and changelog generation, by default we use the popular conventional commits (I think cz-js allows this). -- single package, install one thing and it will work (cz-js is a monorepo, but you have to install different dependencies AFAIK) +They differ a bit in design, not sure if cz-js does any of this, but these are some things you can do with our Commitizen: + +- create custom rules, version bumps and changelog generation. By default, we use the popular conventional commits (I think cz-js allows this). +- single package, install one thing and it will work. cz-js is a monorepo, but you have to install different dependencies as far as I know. - pre-commit integration - works on any language project, as long as you create the `.cz.toml` or `cz.toml` file. @@ -63,9 +70,7 @@ Where do they cross paths? If you are using conventional commits in your git history, then you could swap one with the other in theory. -Regarding the name, [cz-js][cz-js] came first, they used the word commitizen first. When this project was created originally, the creator read "be a good commitizen", and thought it was just a cool word that made sense, and this would be a package that helps you be a good "commit citizen". - -[cz-js]: https://github.com/commitizen/cz-cli +Regarding the name, [cz-js][cz-js] came first, they used the word Commitizen first. When this project was created originally, the creator read "be a good commitizen", and thought it was just a cool word that made sense, and this would be a package that helps you be a good "commit citizen". ## How to handle revert commits? @@ -74,23 +79,37 @@ git revert --no-commit <SHA> git commit -m "revert: foo bar" ``` +## Why don't we use [Pydantic](https://docs.pydantic.dev/)? + +While Pydantic is a powerful and popular library for data validation, we intentionally avoid using it in this project to keep our dependency tree minimal and maintainable. + +Including Pydantic would increase the chances of version conflicts for users - especially with major changes introduced in Pydantic v3. Because we pin dependencies tightly, adding Pydantic could unintentionally restrict what other tools or libraries users can install alongside `commitizen`. + +Moreover we don't rely on the full feature set of Pydantic. Simpler alternatives like Python's built-in `TypedDict` offer sufficient type safety for our use cases, without the runtime overhead or dependency burden. + +In short, avoiding Pydantic helps us: +- Keep dependencies lightweight +- Reduce compatibility issues for users +- Maintain clarity about what contributors should and shouldn't use + + ## I got `Exception [WinError 995] The I/O operation ...` error -This error was caused by a Python bug on Windows. It's been fixed by [this PR](https://github.com/python/cpython/pull/22017), and according to Python's changelog, [3.8.6rc1](https://docs.python.org/3.8/whatsnew/changelog.html#python-3-8-6-release-candidate-1) and [3.9.0rc2](https://docs.python.org/3.9/whatsnew/changelog.html#python-3-9-0-release-candidate-2) should be the accurate versions first contain this fix. In conclusion, upgrade your Python version might solve this issue. +This error was caused by a Python bug on Windows. It's been fixed by [cpython #22017](https://github.com/python/cpython/pull/22017), and according to Python's changelog, [3.8.6rc1](https://docs.python.org/3.8/whatsnew/changelog.html#python-3-8-6-release-candidate-1) and [3.9.0rc2](https://docs.python.org/3.9/whatsnew/changelog.html#python-3-9-0-release-candidate-2) should be the accurate versions first contain this fix. In conclusion, upgrade your Python version might solve this issue. More discussion can be found in issue [#318](https://github.com/commitizen-tools/commitizen/issues/318). -## Why does commitizen not support CalVer? +## Why does Commitizen not support CalVer? `commitizen` could support CalVer alongside SemVer, but in practice implementing CalVer -creates numerous edge cases that are difficult to maintain ([#385]) and more generally +creates numerous edge cases that are difficult to maintain ([#385]) and more generally, mixing the two version schemes may not be a good idea. If CalVer or other custom versioning scheme is needed, `commitizen` could still be used to standardize commits and create changelogs, but a separate package should be used for version increments. Mixing CalVer and SemVer is generally not recommended because each versioning scheme -serves a different purposes. Diverging from either specification can be confusing to -users and cause errors with third party tools that don't expect the non-standard format. +serves a different purpose. Diverging from either specification can be confusing to +users and cause errors with third-party tools that don't expect the non-standard format. In the future, `commitizen` may support some implementation of CalVer, but at the time of writing, there are no plans to implement the feature ([#173]). @@ -110,14 +129,14 @@ If you would like to learn more about both schemes, there are plenty of good res ## How to change the tag format ? -You can use the [`legacy_tag_formats`](config.md#legacy_tag_formats) to list old tag formats. +You can use the [`legacy_tag_formats`](config/bump.md#legacy_tag_formats) to list old tag formats. New bumped tags will be in the new format but old ones will still work for: - changelog generation (full, incremental and version range) - bump new version computation (automatically guessed or increment given) So given if you change from `myproject-$version` to `${version}` and then `v${version}`, -your commitizen configuration will look like this: +your Commitizen configuration will look like this: ```toml tag_format = "v${version}" @@ -129,7 +148,7 @@ legacy_tag_formats = [ ## How to avoid warnings for expected non-version tags -You can explicitly ignore them with [`ignored_tag_formats`](config.md#ignored_tag_formats). +You can explicitly ignore them with [`ignored_tag_formats`](config/bump.md#ignored_tag_formats). ```toml tag_format = "v${version}" @@ -140,3 +159,5 @@ ignored_tag_formats = [ "v${major}.${minor}", ] ``` + +[cz-js]: https://github.com/commitizen/cz-cli diff --git a/docs/features_wont_add.md b/docs/features_wont_add.md new file mode 100644 index 0000000000..9fbd6d5b5c --- /dev/null +++ b/docs/features_wont_add.md @@ -0,0 +1,7 @@ +This page contains features that have been proposed or considered but won't be implemented in Commitizen. + +For a comprehensive list, please refer to our [issue tracker](https://github.com/commitizen-tools/commitizen/issues?q=is:issue%20state:closed%20label:%22issue-status:%20wont-fix%22%20OR%20label:%22issue-status:%20wont-implement%22). + +- Enable multiple locations of config file `.cz.*` [#955](https://github.com/commitizen-tools/commitizen/issues/955) +- Create a flag to build the changelog from commits in multiple git repositories [#790](https://github.com/commitizen-tools/commitizen/issues/790) +- Global Configuration [#597](https://github.com/commitizen-tools/commitizen/issues/597) diff --git a/docs/getting_started.md b/docs/getting_started.md deleted file mode 100644 index 3c6257c363..0000000000 --- a/docs/getting_started.md +++ /dev/null @@ -1,119 +0,0 @@ -## Initialize commitizen - -If it's your first time, you'll need to create a commitizen configuration file. - -The assistant utility will help you set up everything - -```sh -cz init -``` - -Alternatively, create a file `.cz.toml` or `cz.toml` in your project's directory. - -```toml -[tool.commitizen] -version = "0.1.0" -update_changelog_on_bump = true -``` - -## Usage - -### Bump version - -```sh -cz bump -``` - -This command will bump your project's version, and it will create a tag. - -Because of the setting `update_changelog_on_bump`, bump will also create the **changelog**. -You can also [update files](./commands/bump.md#version_files). -You can configure the [version scheme](./commands/bump.md#version_scheme) and [version provider](./config.md#version-providers). - -There are many more options available, please read the docs for the [bump command](./commands/bump.md). - -### Committing - -Run in your terminal - -```bash -cz commit -``` - -or the shortcut - -```bash -cz c -``` - -#### Sign off the commit - -Run in the terminal - -```bash -cz commit -- --signoff -``` - -or the shortcut - -```bash -cz commit -- -s -``` - -### Get project version - -Running `cz version` will return the version of commitizen, but if you want -your project's version you can run: - -```sh -cz version -p -``` - -This can be useful in many situations, where otherwise, you would require a way -to parse the version of your project. Maybe it's simple if you use a `VERSION` file, -but once you start working with many different projects, it becomes tricky. - -A common example is, when you need to send to slack, the changes for the version that you -just created: - -```sh -cz changelog --dry-run "$(cz version -p)" -``` - -### Integration with Pre-commit - -Commitizen can lint your commit message for you with `cz check`. - -You can integrate this in your [pre-commit](https://pre-commit.com/) config with: - -```yaml ---- -repos: - - repo: https://github.com/commitizen-tools/commitizen - rev: master - hooks: - - id: commitizen - - id: commitizen-branch - stages: [pre-push] -``` - -After the configuration is added, you'll need to run: - -```sh -pre-commit install --hook-type commit-msg --hook-type pre-push -``` - -If you aren't using both hooks, you needn't install both stages. - -| Hook | Recommended Stage | -| ----------------- | ----------------- | -| commitizen | commit-msg | -| commitizen-branch | pre-push | - -Note that pre-commit discourages using `master` as a revision, and the above command will print a warning. You should replace the `master` revision with the [latest tag](https://github.com/commitizen-tools/commitizen/tags). This can be done automatically with: - -```sh -pre-commit autoupdate -``` - -Read more about the `check` command [here](commands/check.md). diff --git a/docs/images/cli_help/cz___help.svg b/docs/images/cli_help/cz___help.svg index 098e7df70d..d580cfe619 100644 --- a/docs/images/cli_help/cz___help.svg +++ b/docs/images/cli_help/cz___help.svg @@ -1,4 +1,4 @@ -<svg class="rich-terminal" viewBox="0 0 994 928.4" xmlns="http://www.w3.org/2000/svg"> +<svg class="rich-terminal" viewBox="0 0 994 952.8" xmlns="http://www.w3.org/2000/svg"> <!-- Generated with Rich https://www.textualize.io --> <style> @@ -19,183 +19,187 @@ font-weight: 700; } - .terminal-2205183093-matrix { + .terminal-1389600277-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2205183093-title { + .terminal-1389600277-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2205183093-r1 { fill: #c5c8c6 } -.terminal-2205183093-r2 { fill: #c5c8c6;font-weight: bold } -.terminal-2205183093-r3 { fill: #d0b344 } -.terminal-2205183093-r4 { fill: #1984e9;text-decoration: underline; } -.terminal-2205183093-r5 { fill: #68a0b3;font-weight: bold } + .terminal-1389600277-r1 { fill: #c5c8c6 } +.terminal-1389600277-r2 { fill: #c5c8c6;font-weight: bold } +.terminal-1389600277-r3 { fill: #d0b344 } +.terminal-1389600277-r4 { fill: #1984e9;text-decoration: underline; } +.terminal-1389600277-r5 { fill: #68a0b3;font-weight: bold } </style> <defs> - <clipPath id="terminal-2205183093-clip-terminal"> - <rect x="0" y="0" width="975.0" height="877.4" /> + <clipPath id="terminal-1389600277-clip-terminal"> + <rect x="0" y="0" width="975.0" height="901.8" /> </clipPath> - <clipPath id="terminal-2205183093-line-0"> + <clipPath id="terminal-1389600277-line-0"> <rect x="0" y="1.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-1"> +<clipPath id="terminal-1389600277-line-1"> <rect x="0" y="25.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-2"> +<clipPath id="terminal-1389600277-line-2"> <rect x="0" y="50.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-3"> +<clipPath id="terminal-1389600277-line-3"> <rect x="0" y="74.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-4"> +<clipPath id="terminal-1389600277-line-4"> <rect x="0" y="99.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-5"> +<clipPath id="terminal-1389600277-line-5"> <rect x="0" y="123.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-6"> +<clipPath id="terminal-1389600277-line-6"> <rect x="0" y="147.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-7"> +<clipPath id="terminal-1389600277-line-7"> <rect x="0" y="172.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-8"> +<clipPath id="terminal-1389600277-line-8"> <rect x="0" y="196.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-9"> +<clipPath id="terminal-1389600277-line-9"> <rect x="0" y="221.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-10"> +<clipPath id="terminal-1389600277-line-10"> <rect x="0" y="245.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-11"> +<clipPath id="terminal-1389600277-line-11"> <rect x="0" y="269.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-12"> +<clipPath id="terminal-1389600277-line-12"> <rect x="0" y="294.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-13"> +<clipPath id="terminal-1389600277-line-13"> <rect x="0" y="318.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-14"> +<clipPath id="terminal-1389600277-line-14"> <rect x="0" y="343.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-15"> +<clipPath id="terminal-1389600277-line-15"> <rect x="0" y="367.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-16"> +<clipPath id="terminal-1389600277-line-16"> <rect x="0" y="391.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-17"> +<clipPath id="terminal-1389600277-line-17"> <rect x="0" y="416.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-18"> +<clipPath id="terminal-1389600277-line-18"> <rect x="0" y="440.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-19"> +<clipPath id="terminal-1389600277-line-19"> <rect x="0" y="465.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-20"> +<clipPath id="terminal-1389600277-line-20"> <rect x="0" y="489.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-21"> +<clipPath id="terminal-1389600277-line-21"> <rect x="0" y="513.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-22"> +<clipPath id="terminal-1389600277-line-22"> <rect x="0" y="538.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-23"> +<clipPath id="terminal-1389600277-line-23"> <rect x="0" y="562.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-24"> +<clipPath id="terminal-1389600277-line-24"> <rect x="0" y="587.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-25"> +<clipPath id="terminal-1389600277-line-25"> <rect x="0" y="611.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-26"> +<clipPath id="terminal-1389600277-line-26"> <rect x="0" y="635.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-27"> +<clipPath id="terminal-1389600277-line-27"> <rect x="0" y="660.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-28"> +<clipPath id="terminal-1389600277-line-28"> <rect x="0" y="684.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-29"> +<clipPath id="terminal-1389600277-line-29"> <rect x="0" y="709.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-30"> +<clipPath id="terminal-1389600277-line-30"> <rect x="0" y="733.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-31"> +<clipPath id="terminal-1389600277-line-31"> <rect x="0" y="757.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-32"> +<clipPath id="terminal-1389600277-line-32"> <rect x="0" y="782.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-33"> +<clipPath id="terminal-1389600277-line-33"> <rect x="0" y="806.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-2205183093-line-34"> +<clipPath id="terminal-1389600277-line-34"> <rect x="0" y="831.1" width="976" height="24.65"/> </clipPath> +<clipPath id="terminal-1389600277-line-35"> + <rect x="0" y="855.5" width="976" height="24.65"/> + </clipPath> </defs> - <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="926.4" rx="8"/> + <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="950.8" rx="8"/> <g transform="translate(26,22)"> <circle cx="0" cy="0" r="7" fill="#ff5f57"/> <circle cx="22" cy="0" r="7" fill="#febc2e"/> <circle cx="44" cy="0" r="7" fill="#28c840"/> </g> - <g transform="translate(9, 41)" clip-path="url(#terminal-2205183093-clip-terminal)"> + <g transform="translate(9, 41)" clip-path="url(#terminal-1389600277-clip-terminal)"> - <g class="terminal-2205183093-matrix"> - <text class="terminal-2205183093-r1" x="0" y="20" textLength="134.2" clip-path="url(#terminal-2205183093-line-0)">$ cz --help</text><text class="terminal-2205183093-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-2205183093-line-0)"> -</text><text class="terminal-2205183093-r1" x="0" y="44.4" textLength="122" clip-path="url(#terminal-2205183093-line-1)">usage: cz </text><text class="terminal-2205183093-r2" x="122" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">[</text><text class="terminal-2205183093-r1" x="134.2" y="44.4" textLength="24.4" clip-path="url(#terminal-2205183093-line-1)">-h</text><text class="terminal-2205183093-r2" x="158.6" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">]</text><text class="terminal-2205183093-r2" x="183" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">[</text><text class="terminal-2205183093-r1" x="195.2" y="44.4" textLength="183" clip-path="url(#terminal-2205183093-line-1)">--config CONFIG</text><text class="terminal-2205183093-r2" x="378.2" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">]</text><text class="terminal-2205183093-r2" x="402.6" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">[</text><text class="terminal-2205183093-r1" x="414.8" y="44.4" textLength="85.4" clip-path="url(#terminal-2205183093-line-1)">--debug</text><text class="terminal-2205183093-r2" x="500.2" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">]</text><text class="terminal-2205183093-r2" x="524.6" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">[</text><text class="terminal-2205183093-r1" x="536.8" y="44.4" textLength="85.4" clip-path="url(#terminal-2205183093-line-1)">-n NAME</text><text class="terminal-2205183093-r2" x="622.2" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">]</text><text class="terminal-2205183093-r2" x="646.6" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">[</text><text class="terminal-2205183093-r1" x="658.8" y="44.4" textLength="146.4" clip-path="url(#terminal-2205183093-line-1)">-nr NO_RAISE</text><text class="terminal-2205183093-r2" x="805.2" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)">]</text><text class="terminal-2205183093-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-1)"> -</text><text class="terminal-2205183093-r2" x="122" y="68.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-2)">{</text><text class="terminal-2205183093-r1" x="134.2" y="68.8" textLength="829.6" clip-path="url(#terminal-2205183093-line-2)">init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version</text><text class="terminal-2205183093-r2" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-2)">}</text><text class="terminal-2205183093-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-2)"> -</text><text class="terminal-2205183093-r3" x="0" y="93.2" textLength="36.6" clip-path="url(#terminal-2205183093-line-3)">...</text><text class="terminal-2205183093-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-3)"> -</text><text class="terminal-2205183093-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-4)"> -</text><text class="terminal-2205183093-r1" x="0" y="142" textLength="707.6" clip-path="url(#terminal-2205183093-line-5)">Commitizen is a cli tool to generate conventional commits.</text><text class="terminal-2205183093-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-2205183093-line-5)"> -</text><text class="terminal-2205183093-r1" x="0" y="166.4" textLength="524.6" clip-path="url(#terminal-2205183093-line-6)">For more information about the topic go to </text><text class="terminal-2205183093-r4" x="524.6" y="166.4" textLength="390.4" clip-path="url(#terminal-2205183093-line-6)">https://conventionalcommits.org/</text><text class="terminal-2205183093-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-6)"> -</text><text class="terminal-2205183093-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-7)"> -</text><text class="terminal-2205183093-r1" x="0" y="215.2" textLength="97.6" clip-path="url(#terminal-2205183093-line-8)">options:</text><text class="terminal-2205183093-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-8)"> -</text><text class="terminal-2205183093-r1" x="0" y="239.6" textLength="671" clip-path="url(#terminal-2205183093-line-9)">  -h, --help            show this help message and exit</text><text class="terminal-2205183093-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-9)"> -</text><text class="terminal-2205183093-r1" x="0" y="264" textLength="658.8" clip-path="url(#terminal-2205183093-line-10)">  --config CONFIG       the path of configuration file</text><text class="terminal-2205183093-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-2205183093-line-10)"> -</text><text class="terminal-2205183093-r1" x="0" y="288.4" textLength="463.6" clip-path="url(#terminal-2205183093-line-11)">  --debug               use debug mode</text><text class="terminal-2205183093-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-11)"> -</text><text class="terminal-2205183093-r1" x="0" y="312.8" textLength="597.8" clip-path="url(#terminal-2205183093-line-12)">  -n, --name NAME       use the given commitizen </text><text class="terminal-2205183093-r2" x="597.8" y="312.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-12)">(</text><text class="terminal-2205183093-r1" x="610" y="312.8" textLength="97.6" clip-path="url(#terminal-2205183093-line-12)">default:</text><text class="terminal-2205183093-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-12)"> -</text><text class="terminal-2205183093-r1" x="0" y="337.2" textLength="573.4" clip-path="url(#terminal-2205183093-line-13)">                        cz_conventional_commits</text><text class="terminal-2205183093-r2" x="573.4" y="337.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-13)">)</text><text class="terminal-2205183093-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-13)"> -</text><text class="terminal-2205183093-r1" x="0" y="361.6" textLength="317.2" clip-path="url(#terminal-2205183093-line-14)">  -nr, --no-raise NO_RAISE</text><text class="terminal-2205183093-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-14)"> -</text><text class="terminal-2205183093-r1" x="0" y="386" textLength="902.8" clip-path="url(#terminal-2205183093-line-15)">                        comma separated error codes that won't rise error,</text><text class="terminal-2205183093-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-2205183093-line-15)"> -</text><text class="terminal-2205183093-r1" x="0" y="410.4" textLength="439.2" clip-path="url(#terminal-2205183093-line-16)">                        e.g: cz -nr </text><text class="terminal-2205183093-r5" x="439.2" y="410.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-16)">1</text><text class="terminal-2205183093-r1" x="451.4" y="410.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-16)">,</text><text class="terminal-2205183093-r5" x="463.6" y="410.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-16)">2</text><text class="terminal-2205183093-r1" x="475.8" y="410.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-16)">,</text><text class="terminal-2205183093-r5" x="488" y="410.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-16)">3</text><text class="terminal-2205183093-r1" x="500.2" y="410.4" textLength="231.8" clip-path="url(#terminal-2205183093-line-16)"> bump. See codes at</text><text class="terminal-2205183093-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-16)"> -</text><text class="terminal-2205183093-r4" x="292.8" y="434.8" textLength="231.8" clip-path="url(#terminal-2205183093-line-17)">https://commitizen-</text><text class="terminal-2205183093-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-17)"> -</text><text class="terminal-2205183093-r1" x="0" y="459.2" textLength="756.4" clip-path="url(#terminal-2205183093-line-18)">                        tools.github.io/commitizen/exit_codes/</text><text class="terminal-2205183093-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-18)"> -</text><text class="terminal-2205183093-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-19)"> -</text><text class="terminal-2205183093-r1" x="0" y="508" textLength="109.8" clip-path="url(#terminal-2205183093-line-20)">commands:</text><text class="terminal-2205183093-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-2205183093-line-20)"> -</text><text class="terminal-2205183093-r2" x="24.4" y="532.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-21)">{</text><text class="terminal-2205183093-r1" x="36.6" y="532.4" textLength="829.6" clip-path="url(#terminal-2205183093-line-21)">init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version</text><text class="terminal-2205183093-r2" x="866.2" y="532.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-21)">}</text><text class="terminal-2205183093-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-21)"> -</text><text class="terminal-2205183093-r1" x="0" y="556.8" textLength="646.6" clip-path="url(#terminal-2205183093-line-22)">    init                init commitizen configuration</text><text class="terminal-2205183093-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-22)"> -</text><text class="terminal-2205183093-r1" x="0" y="581.2" textLength="134.2" clip-path="url(#terminal-2205183093-line-23)">    commit </text><text class="terminal-2205183093-r2" x="134.2" y="581.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-23)">(</text><text class="terminal-2205183093-r1" x="146.4" y="581.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-23)">c</text><text class="terminal-2205183093-r2" x="158.6" y="581.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-23)">)</text><text class="terminal-2205183093-r1" x="170.8" y="581.2" textLength="329.4" clip-path="url(#terminal-2205183093-line-23)">          create new commit</text><text class="terminal-2205183093-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-23)"> -</text><text class="terminal-2205183093-r1" x="0" y="605.6" textLength="610" clip-path="url(#terminal-2205183093-line-24)">    ls                  show available commitizens</text><text class="terminal-2205183093-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-24)"> -</text><text class="terminal-2205183093-r1" x="0" y="630" textLength="524.6" clip-path="url(#terminal-2205183093-line-25)">    example             show commit example</text><text class="terminal-2205183093-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-2205183093-line-25)"> -</text><text class="terminal-2205183093-r1" x="0" y="654.4" textLength="646.6" clip-path="url(#terminal-2205183093-line-26)">    info                show information about the cz</text><text class="terminal-2205183093-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-26)"> -</text><text class="terminal-2205183093-r1" x="0" y="678.8" textLength="512.4" clip-path="url(#terminal-2205183093-line-27)">    schema              show commit schema</text><text class="terminal-2205183093-r1" x="976" y="678.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-27)"> -</text><text class="terminal-2205183093-r1" x="0" y="703.2" textLength="805.2" clip-path="url(#terminal-2205183093-line-28)">    bump                bump semantic version based on the git log</text><text class="terminal-2205183093-r1" x="976" y="703.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-28)"> -</text><text class="terminal-2205183093-r1" x="0" y="727.6" textLength="170.8" clip-path="url(#terminal-2205183093-line-29)">    changelog </text><text class="terminal-2205183093-r2" x="170.8" y="727.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-29)">(</text><text class="terminal-2205183093-r1" x="183" y="727.6" textLength="24.4" clip-path="url(#terminal-2205183093-line-29)">ch</text><text class="terminal-2205183093-r2" x="207.4" y="727.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-29)">)</text><text class="terminal-2205183093-r1" x="219.6" y="727.6" textLength="305" clip-path="url(#terminal-2205183093-line-29)">      generate changelog </text><text class="terminal-2205183093-r2" x="524.6" y="727.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-29)">(</text><text class="terminal-2205183093-r1" x="536.8" y="727.6" textLength="329.4" clip-path="url(#terminal-2205183093-line-29)">note that it will overwrite</text><text class="terminal-2205183093-r1" x="976" y="727.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-29)"> -</text><text class="terminal-2205183093-r1" x="0" y="752" textLength="451.4" clip-path="url(#terminal-2205183093-line-30)">                        existing file</text><text class="terminal-2205183093-r2" x="451.4" y="752" textLength="12.2" clip-path="url(#terminal-2205183093-line-30)">)</text><text class="terminal-2205183093-r1" x="976" y="752" textLength="12.2" clip-path="url(#terminal-2205183093-line-30)"> -</text><text class="terminal-2205183093-r1" x="0" y="776.4" textLength="951.6" clip-path="url(#terminal-2205183093-line-31)">    check               validates that a commit message matches the commitizen</text><text class="terminal-2205183093-r1" x="976" y="776.4" textLength="12.2" clip-path="url(#terminal-2205183093-line-31)"> -</text><text class="terminal-2205183093-r1" x="0" y="800.8" textLength="366" clip-path="url(#terminal-2205183093-line-32)">                        schema</text><text class="terminal-2205183093-r1" x="976" y="800.8" textLength="12.2" clip-path="url(#terminal-2205183093-line-32)"> -</text><text class="terminal-2205183093-r1" x="0" y="825.2" textLength="902.8" clip-path="url(#terminal-2205183093-line-33)">    version             get the version of the installed commitizen or the</text><text class="terminal-2205183093-r1" x="976" y="825.2" textLength="12.2" clip-path="url(#terminal-2205183093-line-33)"> -</text><text class="terminal-2205183093-r1" x="0" y="849.6" textLength="488" clip-path="url(#terminal-2205183093-line-34)">                        current project </text><text class="terminal-2205183093-r2" x="488" y="849.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-34)">(</text><text class="terminal-2205183093-r1" x="500.2" y="849.6" textLength="353.8" clip-path="url(#terminal-2205183093-line-34)">default: installed commitizen</text><text class="terminal-2205183093-r2" x="854" y="849.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-34)">)</text><text class="terminal-2205183093-r1" x="976" y="849.6" textLength="12.2" clip-path="url(#terminal-2205183093-line-34)"> -</text><text class="terminal-2205183093-r1" x="976" y="874" textLength="12.2" clip-path="url(#terminal-2205183093-line-35)"> + <g class="terminal-1389600277-matrix"> + <text class="terminal-1389600277-r1" x="0" y="20" textLength="134.2" clip-path="url(#terminal-1389600277-line-0)">$ cz --help</text><text class="terminal-1389600277-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1389600277-line-0)"> +</text><text class="terminal-1389600277-r1" x="0" y="44.4" textLength="122" clip-path="url(#terminal-1389600277-line-1)">usage: cz </text><text class="terminal-1389600277-r2" x="122" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="134.2" y="44.4" textLength="24.4" clip-path="url(#terminal-1389600277-line-1)">-h</text><text class="terminal-1389600277-r2" x="158.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r2" x="183" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="195.2" y="44.4" textLength="183" clip-path="url(#terminal-1389600277-line-1)">--config CONFIG</text><text class="terminal-1389600277-r2" x="378.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r2" x="402.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="414.8" y="44.4" textLength="85.4" clip-path="url(#terminal-1389600277-line-1)">--debug</text><text class="terminal-1389600277-r2" x="500.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r2" x="524.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="536.8" y="44.4" textLength="85.4" clip-path="url(#terminal-1389600277-line-1)">-n NAME</text><text class="terminal-1389600277-r2" x="622.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r2" x="646.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="658.8" y="44.4" textLength="146.4" clip-path="url(#terminal-1389600277-line-1)">-nr NO_RAISE</text><text class="terminal-1389600277-r2" x="805.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)"> +</text><text class="terminal-1389600277-r2" x="122" y="68.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-2)">{</text><text class="terminal-1389600277-r1" x="134.2" y="68.8" textLength="829.6" clip-path="url(#terminal-1389600277-line-2)">init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version</text><text class="terminal-1389600277-r2" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-2)">}</text><text class="terminal-1389600277-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-2)"> +</text><text class="terminal-1389600277-r3" x="0" y="93.2" textLength="36.6" clip-path="url(#terminal-1389600277-line-3)">...</text><text class="terminal-1389600277-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-3)"> +</text><text class="terminal-1389600277-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-4)"> +</text><text class="terminal-1389600277-r1" x="0" y="142" textLength="915" clip-path="url(#terminal-1389600277-line-5)">Commitizen is a powerful release management tool that helps teams maintain </text><text class="terminal-1389600277-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1389600277-line-5)"> +</text><text class="terminal-1389600277-r1" x="0" y="166.4" textLength="951.6" clip-path="url(#terminal-1389600277-line-6)">consistent and meaningful commit messages while automating version management.</text><text class="terminal-1389600277-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-6)"> +</text><text class="terminal-1389600277-r1" x="0" y="190.8" textLength="427" clip-path="url(#terminal-1389600277-line-7)">For more information, please visit </text><text class="terminal-1389600277-r4" x="427" y="190.8" textLength="549" clip-path="url(#terminal-1389600277-line-7)">https://commitizen-tools.github.io/commitizen</text><text class="terminal-1389600277-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-7)"> +</text><text class="terminal-1389600277-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-8)"> +</text><text class="terminal-1389600277-r1" x="0" y="239.6" textLength="97.6" clip-path="url(#terminal-1389600277-line-9)">options:</text><text class="terminal-1389600277-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-9)"> +</text><text class="terminal-1389600277-r1" x="0" y="264" textLength="671" clip-path="url(#terminal-1389600277-line-10)">  -h, --help            show this help message and exit</text><text class="terminal-1389600277-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-1389600277-line-10)"> +</text><text class="terminal-1389600277-r1" x="0" y="288.4" textLength="658.8" clip-path="url(#terminal-1389600277-line-11)">  --config CONFIG       the path of configuration file</text><text class="terminal-1389600277-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-11)"> +</text><text class="terminal-1389600277-r1" x="0" y="312.8" textLength="463.6" clip-path="url(#terminal-1389600277-line-12)">  --debug               use debug mode</text><text class="terminal-1389600277-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-12)"> +</text><text class="terminal-1389600277-r1" x="0" y="337.2" textLength="597.8" clip-path="url(#terminal-1389600277-line-13)">  -n, --name NAME       use the given commitizen </text><text class="terminal-1389600277-r2" x="597.8" y="337.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-13)">(</text><text class="terminal-1389600277-r1" x="610" y="337.2" textLength="97.6" clip-path="url(#terminal-1389600277-line-13)">default:</text><text class="terminal-1389600277-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-13)"> +</text><text class="terminal-1389600277-r1" x="0" y="361.6" textLength="573.4" clip-path="url(#terminal-1389600277-line-14)">                        cz_conventional_commits</text><text class="terminal-1389600277-r2" x="573.4" y="361.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-14)">)</text><text class="terminal-1389600277-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-14)"> +</text><text class="terminal-1389600277-r1" x="0" y="386" textLength="317.2" clip-path="url(#terminal-1389600277-line-15)">  -nr, --no-raise NO_RAISE</text><text class="terminal-1389600277-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-1389600277-line-15)"> +</text><text class="terminal-1389600277-r1" x="0" y="410.4" textLength="915" clip-path="url(#terminal-1389600277-line-16)">                        comma separated error codes that won't raise error,</text><text class="terminal-1389600277-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-16)"> +</text><text class="terminal-1389600277-r1" x="0" y="434.8" textLength="439.2" clip-path="url(#terminal-1389600277-line-17)">                        e.g: cz -nr </text><text class="terminal-1389600277-r5" x="439.2" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">1</text><text class="terminal-1389600277-r1" x="451.4" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">,</text><text class="terminal-1389600277-r5" x="463.6" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">2</text><text class="terminal-1389600277-r1" x="475.8" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">,</text><text class="terminal-1389600277-r5" x="488" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">3</text><text class="terminal-1389600277-r1" x="500.2" y="434.8" textLength="231.8" clip-path="url(#terminal-1389600277-line-17)"> bump. See codes at</text><text class="terminal-1389600277-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)"> +</text><text class="terminal-1389600277-r4" x="292.8" y="459.2" textLength="231.8" clip-path="url(#terminal-1389600277-line-18)">https://commitizen-</text><text class="terminal-1389600277-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-18)"> +</text><text class="terminal-1389600277-r1" x="0" y="483.6" textLength="756.4" clip-path="url(#terminal-1389600277-line-19)">                        tools.github.io/commitizen/exit_codes/</text><text class="terminal-1389600277-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-19)"> +</text><text class="terminal-1389600277-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-1389600277-line-20)"> +</text><text class="terminal-1389600277-r1" x="0" y="532.4" textLength="109.8" clip-path="url(#terminal-1389600277-line-21)">commands:</text><text class="terminal-1389600277-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-21)"> +</text><text class="terminal-1389600277-r2" x="24.4" y="556.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-22)">{</text><text class="terminal-1389600277-r1" x="36.6" y="556.8" textLength="829.6" clip-path="url(#terminal-1389600277-line-22)">init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version</text><text class="terminal-1389600277-r2" x="866.2" y="556.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-22)">}</text><text class="terminal-1389600277-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-22)"> +</text><text class="terminal-1389600277-r1" x="0" y="581.2" textLength="646.6" clip-path="url(#terminal-1389600277-line-23)">    init                init commitizen configuration</text><text class="terminal-1389600277-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-23)"> +</text><text class="terminal-1389600277-r1" x="0" y="605.6" textLength="134.2" clip-path="url(#terminal-1389600277-line-24)">    commit </text><text class="terminal-1389600277-r2" x="134.2" y="605.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-24)">(</text><text class="terminal-1389600277-r1" x="146.4" y="605.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-24)">c</text><text class="terminal-1389600277-r2" x="158.6" y="605.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-24)">)</text><text class="terminal-1389600277-r1" x="170.8" y="605.6" textLength="329.4" clip-path="url(#terminal-1389600277-line-24)">          create new commit</text><text class="terminal-1389600277-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-24)"> +</text><text class="terminal-1389600277-r1" x="0" y="630" textLength="610" clip-path="url(#terminal-1389600277-line-25)">    ls                  show available commitizens</text><text class="terminal-1389600277-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-1389600277-line-25)"> +</text><text class="terminal-1389600277-r1" x="0" y="654.4" textLength="524.6" clip-path="url(#terminal-1389600277-line-26)">    example             show commit example</text><text class="terminal-1389600277-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-26)"> +</text><text class="terminal-1389600277-r1" x="0" y="678.8" textLength="646.6" clip-path="url(#terminal-1389600277-line-27)">    info                show information about the cz</text><text class="terminal-1389600277-r1" x="976" y="678.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-27)"> +</text><text class="terminal-1389600277-r1" x="0" y="703.2" textLength="512.4" clip-path="url(#terminal-1389600277-line-28)">    schema              show commit schema</text><text class="terminal-1389600277-r1" x="976" y="703.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-28)"> +</text><text class="terminal-1389600277-r1" x="0" y="727.6" textLength="805.2" clip-path="url(#terminal-1389600277-line-29)">    bump                bump semantic version based on the git log</text><text class="terminal-1389600277-r1" x="976" y="727.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-29)"> +</text><text class="terminal-1389600277-r1" x="0" y="752" textLength="170.8" clip-path="url(#terminal-1389600277-line-30)">    changelog </text><text class="terminal-1389600277-r2" x="170.8" y="752" textLength="12.2" clip-path="url(#terminal-1389600277-line-30)">(</text><text class="terminal-1389600277-r1" x="183" y="752" textLength="24.4" clip-path="url(#terminal-1389600277-line-30)">ch</text><text class="terminal-1389600277-r2" x="207.4" y="752" textLength="12.2" clip-path="url(#terminal-1389600277-line-30)">)</text><text class="terminal-1389600277-r1" x="219.6" y="752" textLength="305" clip-path="url(#terminal-1389600277-line-30)">      generate changelog </text><text class="terminal-1389600277-r2" x="524.6" y="752" textLength="12.2" clip-path="url(#terminal-1389600277-line-30)">(</text><text class="terminal-1389600277-r1" x="536.8" y="752" textLength="329.4" clip-path="url(#terminal-1389600277-line-30)">note that it will overwrite</text><text class="terminal-1389600277-r1" x="976" y="752" textLength="12.2" clip-path="url(#terminal-1389600277-line-30)"> +</text><text class="terminal-1389600277-r1" x="0" y="776.4" textLength="451.4" clip-path="url(#terminal-1389600277-line-31)">                        existing file</text><text class="terminal-1389600277-r2" x="451.4" y="776.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-31)">)</text><text class="terminal-1389600277-r1" x="976" y="776.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-31)"> +</text><text class="terminal-1389600277-r1" x="0" y="800.8" textLength="951.6" clip-path="url(#terminal-1389600277-line-32)">    check               validates that a commit message matches the commitizen</text><text class="terminal-1389600277-r1" x="976" y="800.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-32)"> +</text><text class="terminal-1389600277-r1" x="0" y="825.2" textLength="366" clip-path="url(#terminal-1389600277-line-33)">                        schema</text><text class="terminal-1389600277-r1" x="976" y="825.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-33)"> +</text><text class="terminal-1389600277-r1" x="0" y="849.6" textLength="902.8" clip-path="url(#terminal-1389600277-line-34)">    version             get the version of the installed commitizen or the</text><text class="terminal-1389600277-r1" x="976" y="849.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-34)"> +</text><text class="terminal-1389600277-r1" x="0" y="874" textLength="488" clip-path="url(#terminal-1389600277-line-35)">                        current project </text><text class="terminal-1389600277-r2" x="488" y="874" textLength="12.2" clip-path="url(#terminal-1389600277-line-35)">(</text><text class="terminal-1389600277-r1" x="500.2" y="874" textLength="353.8" clip-path="url(#terminal-1389600277-line-35)">default: installed commitizen</text><text class="terminal-1389600277-r2" x="854" y="874" textLength="12.2" clip-path="url(#terminal-1389600277-line-35)">)</text><text class="terminal-1389600277-r1" x="976" y="874" textLength="12.2" clip-path="url(#terminal-1389600277-line-35)"> +</text><text class="terminal-1389600277-r1" x="976" y="898.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-36)"> </text> </g> </g> diff --git a/docs/images/cli_help/cz_bump___help.svg b/docs/images/cli_help/cz_bump___help.svg index 7f27636ddf..7d007df312 100644 --- a/docs/images/cli_help/cz_bump___help.svg +++ b/docs/images/cli_help/cz_bump___help.svg @@ -19,273 +19,273 @@ font-weight: 700; } - .terminal-243650528-matrix { + .terminal-1655558888-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-243650528-title { + .terminal-1655558888-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-243650528-r1 { fill: #c5c8c6 } -.terminal-243650528-r2 { fill: #c5c8c6;font-weight: bold } -.terminal-243650528-r3 { fill: #68a0b3;font-weight: bold } -.terminal-243650528-r4 { fill: #98a84b } + .terminal-1655558888-r1 { fill: #c5c8c6 } +.terminal-1655558888-r2 { fill: #c5c8c6;font-weight: bold } +.terminal-1655558888-r3 { fill: #68a0b3;font-weight: bold } +.terminal-1655558888-r4 { fill: #98a84b } </style> <defs> - <clipPath id="terminal-243650528-clip-terminal"> + <clipPath id="terminal-1655558888-clip-terminal"> <rect x="0" y="0" width="975.0" height="2024.1999999999998" /> </clipPath> - <clipPath id="terminal-243650528-line-0"> + <clipPath id="terminal-1655558888-line-0"> <rect x="0" y="1.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-1"> +<clipPath id="terminal-1655558888-line-1"> <rect x="0" y="25.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-2"> +<clipPath id="terminal-1655558888-line-2"> <rect x="0" y="50.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-3"> +<clipPath id="terminal-1655558888-line-3"> <rect x="0" y="74.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-4"> +<clipPath id="terminal-1655558888-line-4"> <rect x="0" y="99.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-5"> +<clipPath id="terminal-1655558888-line-5"> <rect x="0" y="123.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-6"> +<clipPath id="terminal-1655558888-line-6"> <rect x="0" y="147.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-7"> +<clipPath id="terminal-1655558888-line-7"> <rect x="0" y="172.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-8"> +<clipPath id="terminal-1655558888-line-8"> <rect x="0" y="196.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-9"> +<clipPath id="terminal-1655558888-line-9"> <rect x="0" y="221.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-10"> +<clipPath id="terminal-1655558888-line-10"> <rect x="0" y="245.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-11"> +<clipPath id="terminal-1655558888-line-11"> <rect x="0" y="269.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-12"> +<clipPath id="terminal-1655558888-line-12"> <rect x="0" y="294.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-13"> +<clipPath id="terminal-1655558888-line-13"> <rect x="0" y="318.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-14"> +<clipPath id="terminal-1655558888-line-14"> <rect x="0" y="343.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-15"> +<clipPath id="terminal-1655558888-line-15"> <rect x="0" y="367.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-16"> +<clipPath id="terminal-1655558888-line-16"> <rect x="0" y="391.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-17"> +<clipPath id="terminal-1655558888-line-17"> <rect x="0" y="416.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-18"> +<clipPath id="terminal-1655558888-line-18"> <rect x="0" y="440.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-19"> +<clipPath id="terminal-1655558888-line-19"> <rect x="0" y="465.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-20"> +<clipPath id="terminal-1655558888-line-20"> <rect x="0" y="489.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-21"> +<clipPath id="terminal-1655558888-line-21"> <rect x="0" y="513.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-22"> +<clipPath id="terminal-1655558888-line-22"> <rect x="0" y="538.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-23"> +<clipPath id="terminal-1655558888-line-23"> <rect x="0" y="562.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-24"> +<clipPath id="terminal-1655558888-line-24"> <rect x="0" y="587.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-25"> +<clipPath id="terminal-1655558888-line-25"> <rect x="0" y="611.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-26"> +<clipPath id="terminal-1655558888-line-26"> <rect x="0" y="635.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-27"> +<clipPath id="terminal-1655558888-line-27"> <rect x="0" y="660.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-28"> +<clipPath id="terminal-1655558888-line-28"> <rect x="0" y="684.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-29"> +<clipPath id="terminal-1655558888-line-29"> <rect x="0" y="709.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-30"> +<clipPath id="terminal-1655558888-line-30"> <rect x="0" y="733.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-31"> +<clipPath id="terminal-1655558888-line-31"> <rect x="0" y="757.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-32"> +<clipPath id="terminal-1655558888-line-32"> <rect x="0" y="782.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-33"> +<clipPath id="terminal-1655558888-line-33"> <rect x="0" y="806.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-34"> +<clipPath id="terminal-1655558888-line-34"> <rect x="0" y="831.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-35"> +<clipPath id="terminal-1655558888-line-35"> <rect x="0" y="855.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-36"> +<clipPath id="terminal-1655558888-line-36"> <rect x="0" y="879.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-37"> +<clipPath id="terminal-1655558888-line-37"> <rect x="0" y="904.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-38"> +<clipPath id="terminal-1655558888-line-38"> <rect x="0" y="928.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-39"> +<clipPath id="terminal-1655558888-line-39"> <rect x="0" y="953.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-40"> +<clipPath id="terminal-1655558888-line-40"> <rect x="0" y="977.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-41"> +<clipPath id="terminal-1655558888-line-41"> <rect x="0" y="1001.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-42"> +<clipPath id="terminal-1655558888-line-42"> <rect x="0" y="1026.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-43"> +<clipPath id="terminal-1655558888-line-43"> <rect x="0" y="1050.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-44"> +<clipPath id="terminal-1655558888-line-44"> <rect x="0" y="1075.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-45"> +<clipPath id="terminal-1655558888-line-45"> <rect x="0" y="1099.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-46"> +<clipPath id="terminal-1655558888-line-46"> <rect x="0" y="1123.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-47"> +<clipPath id="terminal-1655558888-line-47"> <rect x="0" y="1148.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-48"> +<clipPath id="terminal-1655558888-line-48"> <rect x="0" y="1172.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-49"> +<clipPath id="terminal-1655558888-line-49"> <rect x="0" y="1197.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-50"> +<clipPath id="terminal-1655558888-line-50"> <rect x="0" y="1221.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-51"> +<clipPath id="terminal-1655558888-line-51"> <rect x="0" y="1245.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-52"> +<clipPath id="terminal-1655558888-line-52"> <rect x="0" y="1270.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-53"> +<clipPath id="terminal-1655558888-line-53"> <rect x="0" y="1294.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-54"> +<clipPath id="terminal-1655558888-line-54"> <rect x="0" y="1319.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-55"> +<clipPath id="terminal-1655558888-line-55"> <rect x="0" y="1343.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-56"> +<clipPath id="terminal-1655558888-line-56"> <rect x="0" y="1367.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-57"> +<clipPath id="terminal-1655558888-line-57"> <rect x="0" y="1392.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-58"> +<clipPath id="terminal-1655558888-line-58"> <rect x="0" y="1416.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-59"> +<clipPath id="terminal-1655558888-line-59"> <rect x="0" y="1441.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-60"> +<clipPath id="terminal-1655558888-line-60"> <rect x="0" y="1465.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-61"> +<clipPath id="terminal-1655558888-line-61"> <rect x="0" y="1489.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-62"> +<clipPath id="terminal-1655558888-line-62"> <rect x="0" y="1514.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-63"> +<clipPath id="terminal-1655558888-line-63"> <rect x="0" y="1538.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-64"> +<clipPath id="terminal-1655558888-line-64"> <rect x="0" y="1563.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-65"> +<clipPath id="terminal-1655558888-line-65"> <rect x="0" y="1587.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-66"> +<clipPath id="terminal-1655558888-line-66"> <rect x="0" y="1611.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-67"> +<clipPath id="terminal-1655558888-line-67"> <rect x="0" y="1636.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-68"> +<clipPath id="terminal-1655558888-line-68"> <rect x="0" y="1660.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-69"> +<clipPath id="terminal-1655558888-line-69"> <rect x="0" y="1685.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-70"> +<clipPath id="terminal-1655558888-line-70"> <rect x="0" y="1709.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-71"> +<clipPath id="terminal-1655558888-line-71"> <rect x="0" y="1733.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-72"> +<clipPath id="terminal-1655558888-line-72"> <rect x="0" y="1758.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-73"> +<clipPath id="terminal-1655558888-line-73"> <rect x="0" y="1782.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-74"> +<clipPath id="terminal-1655558888-line-74"> <rect x="0" y="1807.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-75"> +<clipPath id="terminal-1655558888-line-75"> <rect x="0" y="1831.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-76"> +<clipPath id="terminal-1655558888-line-76"> <rect x="0" y="1855.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-77"> +<clipPath id="terminal-1655558888-line-77"> <rect x="0" y="1880.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-78"> +<clipPath id="terminal-1655558888-line-78"> <rect x="0" y="1904.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-79"> +<clipPath id="terminal-1655558888-line-79"> <rect x="0" y="1929.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-80"> +<clipPath id="terminal-1655558888-line-80"> <rect x="0" y="1953.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-243650528-line-81"> +<clipPath id="terminal-1655558888-line-81"> <rect x="0" y="1977.9" width="976" height="24.65"/> </clipPath> </defs> @@ -297,92 +297,92 @@ <circle cx="44" cy="0" r="7" fill="#28c840"/> </g> - <g transform="translate(9, 41)" clip-path="url(#terminal-243650528-clip-terminal)"> + <g transform="translate(9, 41)" clip-path="url(#terminal-1655558888-clip-terminal)"> - <g class="terminal-243650528-matrix"> - <text class="terminal-243650528-r1" x="0" y="20" textLength="195.2" clip-path="url(#terminal-243650528-line-0)">$ cz bump --help</text><text class="terminal-243650528-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-243650528-line-0)"> -</text><text class="terminal-243650528-r1" x="0" y="44.4" textLength="183" clip-path="url(#terminal-243650528-line-1)">usage: cz bump </text><text class="terminal-243650528-r2" x="183" y="44.4" textLength="12.2" clip-path="url(#terminal-243650528-line-1)">[</text><text class="terminal-243650528-r1" x="195.2" y="44.4" textLength="24.4" clip-path="url(#terminal-243650528-line-1)">-h</text><text class="terminal-243650528-r2" x="219.6" y="44.4" textLength="12.2" clip-path="url(#terminal-243650528-line-1)">]</text><text class="terminal-243650528-r2" x="244" y="44.4" textLength="12.2" clip-path="url(#terminal-243650528-line-1)">[</text><text class="terminal-243650528-r1" x="256.2" y="44.4" textLength="109.8" clip-path="url(#terminal-243650528-line-1)">--dry-run</text><text class="terminal-243650528-r2" x="366" y="44.4" textLength="12.2" clip-path="url(#terminal-243650528-line-1)">]</text><text class="terminal-243650528-r2" x="390.4" y="44.4" textLength="12.2" clip-path="url(#terminal-243650528-line-1)">[</text><text class="terminal-243650528-r1" x="402.6" y="44.4" textLength="146.4" clip-path="url(#terminal-243650528-line-1)">--files-only</text><text class="terminal-243650528-r2" x="549" y="44.4" textLength="12.2" clip-path="url(#terminal-243650528-line-1)">]</text><text class="terminal-243650528-r2" x="573.4" y="44.4" textLength="12.2" clip-path="url(#terminal-243650528-line-1)">[</text><text class="terminal-243650528-r1" x="585.6" y="44.4" textLength="183" clip-path="url(#terminal-243650528-line-1)">--local-version</text><text class="terminal-243650528-r2" x="768.6" y="44.4" textLength="12.2" clip-path="url(#terminal-243650528-line-1)">]</text><text class="terminal-243650528-r2" x="793" y="44.4" textLength="12.2" clip-path="url(#terminal-243650528-line-1)">[</text><text class="terminal-243650528-r1" x="805.2" y="44.4" textLength="134.2" clip-path="url(#terminal-243650528-line-1)">--changelog</text><text class="terminal-243650528-r2" x="939.4" y="44.4" textLength="12.2" clip-path="url(#terminal-243650528-line-1)">]</text><text class="terminal-243650528-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-243650528-line-1)"> -</text><text class="terminal-243650528-r2" x="183" y="68.8" textLength="12.2" clip-path="url(#terminal-243650528-line-2)">[</text><text class="terminal-243650528-r1" x="195.2" y="68.8" textLength="134.2" clip-path="url(#terminal-243650528-line-2)">--no-verify</text><text class="terminal-243650528-r2" x="329.4" y="68.8" textLength="12.2" clip-path="url(#terminal-243650528-line-2)">]</text><text class="terminal-243650528-r2" x="353.8" y="68.8" textLength="12.2" clip-path="url(#terminal-243650528-line-2)">[</text><text class="terminal-243650528-r1" x="366" y="68.8" textLength="61" clip-path="url(#terminal-243650528-line-2)">--yes</text><text class="terminal-243650528-r2" x="427" y="68.8" textLength="12.2" clip-path="url(#terminal-243650528-line-2)">]</text><text class="terminal-243650528-r2" x="451.4" y="68.8" textLength="12.2" clip-path="url(#terminal-243650528-line-2)">[</text><text class="terminal-243650528-r1" x="463.6" y="68.8" textLength="280.6" clip-path="url(#terminal-243650528-line-2)">--tag-format TAG_FORMAT</text><text class="terminal-243650528-r2" x="744.2" y="68.8" textLength="12.2" clip-path="url(#terminal-243650528-line-2)">]</text><text class="terminal-243650528-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-243650528-line-2)"> -</text><text class="terminal-243650528-r2" x="183" y="93.2" textLength="12.2" clip-path="url(#terminal-243650528-line-3)">[</text><text class="terminal-243650528-r1" x="195.2" y="93.2" textLength="329.4" clip-path="url(#terminal-243650528-line-3)">--bump-message BUMP_MESSAGE</text><text class="terminal-243650528-r2" x="524.6" y="93.2" textLength="12.2" clip-path="url(#terminal-243650528-line-3)">]</text><text class="terminal-243650528-r2" x="549" y="93.2" textLength="12.2" clip-path="url(#terminal-243650528-line-3)">[</text><text class="terminal-243650528-r1" x="561.2" y="93.2" textLength="158.6" clip-path="url(#terminal-243650528-line-3)">--prerelease </text><text class="terminal-243650528-r2" x="719.8" y="93.2" textLength="12.2" clip-path="url(#terminal-243650528-line-3)">{</text><text class="terminal-243650528-r1" x="732" y="93.2" textLength="158.6" clip-path="url(#terminal-243650528-line-3)">alpha,beta,rc</text><text class="terminal-243650528-r2" x="890.6" y="93.2" textLength="12.2" clip-path="url(#terminal-243650528-line-3)">}</text><text class="terminal-243650528-r2" x="902.8" y="93.2" textLength="12.2" clip-path="url(#terminal-243650528-line-3)">]</text><text class="terminal-243650528-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-243650528-line-3)"> -</text><text class="terminal-243650528-r2" x="183" y="117.6" textLength="12.2" clip-path="url(#terminal-243650528-line-4)">[</text><text class="terminal-243650528-r1" x="195.2" y="117.6" textLength="280.6" clip-path="url(#terminal-243650528-line-4)">--devrelease DEVRELEASE</text><text class="terminal-243650528-r2" x="475.8" y="117.6" textLength="12.2" clip-path="url(#terminal-243650528-line-4)">]</text><text class="terminal-243650528-r2" x="500.2" y="117.6" textLength="12.2" clip-path="url(#terminal-243650528-line-4)">[</text><text class="terminal-243650528-r1" x="512.4" y="117.6" textLength="146.4" clip-path="url(#terminal-243650528-line-4)">--increment </text><text class="terminal-243650528-r2" x="658.8" y="117.6" textLength="12.2" clip-path="url(#terminal-243650528-line-4)">{</text><text class="terminal-243650528-r1" x="671" y="117.6" textLength="207.4" clip-path="url(#terminal-243650528-line-4)">MAJOR,MINOR,PATCH</text><text class="terminal-243650528-r2" x="878.4" y="117.6" textLength="12.2" clip-path="url(#terminal-243650528-line-4)">}</text><text class="terminal-243650528-r2" x="890.6" y="117.6" textLength="12.2" clip-path="url(#terminal-243650528-line-4)">]</text><text class="terminal-243650528-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-243650528-line-4)"> -</text><text class="terminal-243650528-r2" x="183" y="142" textLength="12.2" clip-path="url(#terminal-243650528-line-5)">[</text><text class="terminal-243650528-r1" x="195.2" y="142" textLength="207.4" clip-path="url(#terminal-243650528-line-5)">--increment-mode </text><text class="terminal-243650528-r2" x="402.6" y="142" textLength="12.2" clip-path="url(#terminal-243650528-line-5)">{</text><text class="terminal-243650528-r1" x="414.8" y="142" textLength="146.4" clip-path="url(#terminal-243650528-line-5)">linear,exact</text><text class="terminal-243650528-r2" x="561.2" y="142" textLength="12.2" clip-path="url(#terminal-243650528-line-5)">}</text><text class="terminal-243650528-r2" x="573.4" y="142" textLength="12.2" clip-path="url(#terminal-243650528-line-5)">]</text><text class="terminal-243650528-r2" x="597.8" y="142" textLength="12.2" clip-path="url(#terminal-243650528-line-5)">[</text><text class="terminal-243650528-r1" x="610" y="142" textLength="231.8" clip-path="url(#terminal-243650528-line-5)">--check-consistency</text><text class="terminal-243650528-r2" x="841.8" y="142" textLength="12.2" clip-path="url(#terminal-243650528-line-5)">]</text><text class="terminal-243650528-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-243650528-line-5)"> -</text><text class="terminal-243650528-r2" x="183" y="166.4" textLength="12.2" clip-path="url(#terminal-243650528-line-6)">[</text><text class="terminal-243650528-r1" x="195.2" y="166.4" textLength="183" clip-path="url(#terminal-243650528-line-6)">--annotated-tag</text><text class="terminal-243650528-r2" x="378.2" y="166.4" textLength="12.2" clip-path="url(#terminal-243650528-line-6)">]</text><text class="terminal-243650528-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-243650528-line-6)"> -</text><text class="terminal-243650528-r2" x="183" y="190.8" textLength="12.2" clip-path="url(#terminal-243650528-line-7)">[</text><text class="terminal-243650528-r1" x="195.2" y="190.8" textLength="549" clip-path="url(#terminal-243650528-line-7)">--annotated-tag-message ANNOTATED_TAG_MESSAGE</text><text class="terminal-243650528-r2" x="744.2" y="190.8" textLength="12.2" clip-path="url(#terminal-243650528-line-7)">]</text><text class="terminal-243650528-r2" x="768.6" y="190.8" textLength="12.2" clip-path="url(#terminal-243650528-line-7)">[</text><text class="terminal-243650528-r1" x="780.8" y="190.8" textLength="122" clip-path="url(#terminal-243650528-line-7)">--gpg-sign</text><text class="terminal-243650528-r2" x="902.8" y="190.8" textLength="12.2" clip-path="url(#terminal-243650528-line-7)">]</text><text class="terminal-243650528-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-243650528-line-7)"> -</text><text class="terminal-243650528-r2" x="183" y="215.2" textLength="12.2" clip-path="url(#terminal-243650528-line-8)">[</text><text class="terminal-243650528-r1" x="195.2" y="215.2" textLength="256.2" clip-path="url(#terminal-243650528-line-8)">--changelog-to-stdout</text><text class="terminal-243650528-r2" x="451.4" y="215.2" textLength="12.2" clip-path="url(#terminal-243650528-line-8)">]</text><text class="terminal-243650528-r2" x="475.8" y="215.2" textLength="12.2" clip-path="url(#terminal-243650528-line-8)">[</text><text class="terminal-243650528-r1" x="488" y="215.2" textLength="268.4" clip-path="url(#terminal-243650528-line-8)">--git-output-to-stderr</text><text class="terminal-243650528-r2" x="756.4" y="215.2" textLength="12.2" clip-path="url(#terminal-243650528-line-8)">]</text><text class="terminal-243650528-r2" x="780.8" y="215.2" textLength="12.2" clip-path="url(#terminal-243650528-line-8)">[</text><text class="terminal-243650528-r1" x="793" y="215.2" textLength="85.4" clip-path="url(#terminal-243650528-line-8)">--retry</text><text class="terminal-243650528-r2" x="878.4" y="215.2" textLength="12.2" clip-path="url(#terminal-243650528-line-8)">]</text><text class="terminal-243650528-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-243650528-line-8)"> -</text><text class="terminal-243650528-r2" x="183" y="239.6" textLength="12.2" clip-path="url(#terminal-243650528-line-9)">[</text><text class="terminal-243650528-r1" x="195.2" y="239.6" textLength="244" clip-path="url(#terminal-243650528-line-9)">--major-version-zero</text><text class="terminal-243650528-r2" x="439.2" y="239.6" textLength="12.2" clip-path="url(#terminal-243650528-line-9)">]</text><text class="terminal-243650528-r2" x="463.6" y="239.6" textLength="12.2" clip-path="url(#terminal-243650528-line-9)">[</text><text class="terminal-243650528-r1" x="475.8" y="239.6" textLength="231.8" clip-path="url(#terminal-243650528-line-9)">--template TEMPLATE</text><text class="terminal-243650528-r2" x="707.6" y="239.6" textLength="12.2" clip-path="url(#terminal-243650528-line-9)">]</text><text class="terminal-243650528-r2" x="732" y="239.6" textLength="12.2" clip-path="url(#terminal-243650528-line-9)">[</text><text class="terminal-243650528-r1" x="744.2" y="239.6" textLength="158.6" clip-path="url(#terminal-243650528-line-9)">--extra EXTRA</text><text class="terminal-243650528-r2" x="902.8" y="239.6" textLength="12.2" clip-path="url(#terminal-243650528-line-9)">]</text><text class="terminal-243650528-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-243650528-line-9)"> -</text><text class="terminal-243650528-r2" x="183" y="264" textLength="12.2" clip-path="url(#terminal-243650528-line-10)">[</text><text class="terminal-243650528-r1" x="195.2" y="264" textLength="256.2" clip-path="url(#terminal-243650528-line-10)">--file-name FILE_NAME</text><text class="terminal-243650528-r2" x="451.4" y="264" textLength="12.2" clip-path="url(#terminal-243650528-line-10)">]</text><text class="terminal-243650528-r2" x="475.8" y="264" textLength="12.2" clip-path="url(#terminal-243650528-line-10)">[</text><text class="terminal-243650528-r1" x="488" y="264" textLength="451.4" clip-path="url(#terminal-243650528-line-10)">--prerelease-offset PRERELEASE_OFFSET</text><text class="terminal-243650528-r2" x="939.4" y="264" textLength="12.2" clip-path="url(#terminal-243650528-line-10)">]</text><text class="terminal-243650528-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-243650528-line-10)"> -</text><text class="terminal-243650528-r2" x="183" y="288.4" textLength="12.2" clip-path="url(#terminal-243650528-line-11)">[</text><text class="terminal-243650528-r1" x="195.2" y="288.4" textLength="207.4" clip-path="url(#terminal-243650528-line-11)">--version-scheme </text><text class="terminal-243650528-r2" x="402.6" y="288.4" textLength="12.2" clip-path="url(#terminal-243650528-line-11)">{</text><text class="terminal-243650528-r1" x="414.8" y="288.4" textLength="256.2" clip-path="url(#terminal-243650528-line-11)">pep440,semver,semver2</text><text class="terminal-243650528-r2" x="671" y="288.4" textLength="12.2" clip-path="url(#terminal-243650528-line-11)">}</text><text class="terminal-243650528-r2" x="683.2" y="288.4" textLength="12.2" clip-path="url(#terminal-243650528-line-11)">]</text><text class="terminal-243650528-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-243650528-line-11)"> -</text><text class="terminal-243650528-r2" x="183" y="312.8" textLength="12.2" clip-path="url(#terminal-243650528-line-12)">[</text><text class="terminal-243650528-r1" x="195.2" y="312.8" textLength="183" clip-path="url(#terminal-243650528-line-12)">--version-type </text><text class="terminal-243650528-r2" x="378.2" y="312.8" textLength="12.2" clip-path="url(#terminal-243650528-line-12)">{</text><text class="terminal-243650528-r1" x="390.4" y="312.8" textLength="256.2" clip-path="url(#terminal-243650528-line-12)">pep440,semver,semver2</text><text class="terminal-243650528-r2" x="646.6" y="312.8" textLength="12.2" clip-path="url(#terminal-243650528-line-12)">}</text><text class="terminal-243650528-r2" x="658.8" y="312.8" textLength="12.2" clip-path="url(#terminal-243650528-line-12)">]</text><text class="terminal-243650528-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-243650528-line-12)"> -</text><text class="terminal-243650528-r2" x="183" y="337.2" textLength="12.2" clip-path="url(#terminal-243650528-line-13)">[</text><text class="terminal-243650528-r1" x="195.2" y="337.2" textLength="378.2" clip-path="url(#terminal-243650528-line-13)">--build-metadata BUILD_METADATA</text><text class="terminal-243650528-r2" x="573.4" y="337.2" textLength="12.2" clip-path="url(#terminal-243650528-line-13)">]</text><text class="terminal-243650528-r2" x="597.8" y="337.2" textLength="12.2" clip-path="url(#terminal-243650528-line-13)">[</text><text class="terminal-243650528-r1" x="610" y="337.2" textLength="122" clip-path="url(#terminal-243650528-line-13)">--get-next</text><text class="terminal-243650528-r2" x="732" y="337.2" textLength="12.2" clip-path="url(#terminal-243650528-line-13)">]</text><text class="terminal-243650528-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-243650528-line-13)"> -</text><text class="terminal-243650528-r2" x="183" y="361.6" textLength="12.2" clip-path="url(#terminal-243650528-line-14)">[</text><text class="terminal-243650528-r1" x="195.2" y="361.6" textLength="207.4" clip-path="url(#terminal-243650528-line-14)">--allow-no-commit</text><text class="terminal-243650528-r2" x="402.6" y="361.6" textLength="12.2" clip-path="url(#terminal-243650528-line-14)">]</text><text class="terminal-243650528-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-243650528-line-14)"> -</text><text class="terminal-243650528-r2" x="183" y="386" textLength="12.2" clip-path="url(#terminal-243650528-line-15)">[</text><text class="terminal-243650528-r1" x="195.2" y="386" textLength="170.8" clip-path="url(#terminal-243650528-line-15)">MANUAL_VERSION</text><text class="terminal-243650528-r2" x="366" y="386" textLength="12.2" clip-path="url(#terminal-243650528-line-15)">]</text><text class="terminal-243650528-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-243650528-line-15)"> -</text><text class="terminal-243650528-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-243650528-line-16)"> -</text><text class="terminal-243650528-r1" x="0" y="434.8" textLength="512.4" clip-path="url(#terminal-243650528-line-17)">bump semantic version based on the git log</text><text class="terminal-243650528-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-243650528-line-17)"> -</text><text class="terminal-243650528-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-243650528-line-18)"> -</text><text class="terminal-243650528-r1" x="0" y="483.6" textLength="256.2" clip-path="url(#terminal-243650528-line-19)">positional arguments:</text><text class="terminal-243650528-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-243650528-line-19)"> -</text><text class="terminal-243650528-r1" x="0" y="508" textLength="610" clip-path="url(#terminal-243650528-line-20)">  MANUAL_VERSION        bump to the given version </text><text class="terminal-243650528-r2" x="610" y="508" textLength="12.2" clip-path="url(#terminal-243650528-line-20)">(</text><text class="terminal-243650528-r1" x="622.2" y="508" textLength="61" clip-path="url(#terminal-243650528-line-20)">e.g: </text><text class="terminal-243650528-r3" x="683.2" y="508" textLength="36.6" clip-path="url(#terminal-243650528-line-20)">1.5</text><text class="terminal-243650528-r1" x="719.8" y="508" textLength="12.2" clip-path="url(#terminal-243650528-line-20)">.</text><text class="terminal-243650528-r3" x="732" y="508" textLength="12.2" clip-path="url(#terminal-243650528-line-20)">3</text><text class="terminal-243650528-r2" x="744.2" y="508" textLength="12.2" clip-path="url(#terminal-243650528-line-20)">)</text><text class="terminal-243650528-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-243650528-line-20)"> -</text><text class="terminal-243650528-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-243650528-line-21)"> -</text><text class="terminal-243650528-r1" x="0" y="556.8" textLength="97.6" clip-path="url(#terminal-243650528-line-22)">options:</text><text class="terminal-243650528-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-243650528-line-22)"> -</text><text class="terminal-243650528-r1" x="0" y="581.2" textLength="671" clip-path="url(#terminal-243650528-line-23)">  -h, --help            show this help message and exit</text><text class="terminal-243650528-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-243650528-line-23)"> -</text><text class="terminal-243650528-r1" x="0" y="605.6" textLength="915" clip-path="url(#terminal-243650528-line-24)">  --dry-run             show output to stdout, no commit, no modified files</text><text class="terminal-243650528-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-243650528-line-24)"> -</text><text class="terminal-243650528-r1" x="0" y="630" textLength="793" clip-path="url(#terminal-243650528-line-25)">  --files-only          bump version in the files from the config</text><text class="terminal-243650528-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-243650528-line-25)"> -</text><text class="terminal-243650528-r1" x="0" y="654.4" textLength="719.8" clip-path="url(#terminal-243650528-line-26)">  --local-version       bump only the local version portion</text><text class="terminal-243650528-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-243650528-line-26)"> -</text><text class="terminal-243650528-r1" x="0" y="678.8" textLength="841.8" clip-path="url(#terminal-243650528-line-27)">  --changelog, -ch      generate the changelog for the newest version</text><text class="terminal-243650528-r1" x="976" y="678.8" textLength="12.2" clip-path="url(#terminal-243650528-line-27)"> -</text><text class="terminal-243650528-r1" x="0" y="703.2" textLength="902.8" clip-path="url(#terminal-243650528-line-28)">  --no-verify           this option bypasses the pre-commit and commit-msg</text><text class="terminal-243650528-r1" x="976" y="703.2" textLength="12.2" clip-path="url(#terminal-243650528-line-28)"> -</text><text class="terminal-243650528-r1" x="0" y="727.6" textLength="353.8" clip-path="url(#terminal-243650528-line-29)">                        hooks</text><text class="terminal-243650528-r1" x="976" y="727.6" textLength="12.2" clip-path="url(#terminal-243650528-line-29)"> -</text><text class="terminal-243650528-r1" x="0" y="752" textLength="719.8" clip-path="url(#terminal-243650528-line-30)">  --yes                 accept automatically questions done</text><text class="terminal-243650528-r1" x="976" y="752" textLength="12.2" clip-path="url(#terminal-243650528-line-30)"> -</text><text class="terminal-243650528-r1" x="0" y="776.4" textLength="305" clip-path="url(#terminal-243650528-line-31)">  --tag-format TAG_FORMAT</text><text class="terminal-243650528-r1" x="976" y="776.4" textLength="12.2" clip-path="url(#terminal-243650528-line-31)"> -</text><text class="terminal-243650528-r1" x="0" y="800.8" textLength="939.4" clip-path="url(#terminal-243650528-line-32)">                        the format used to tag the commit and read it, use it</text><text class="terminal-243650528-r1" x="976" y="800.8" textLength="12.2" clip-path="url(#terminal-243650528-line-32)"> -</text><text class="terminal-243650528-r1" x="0" y="825.2" textLength="866.2" clip-path="url(#terminal-243650528-line-33)">                        in existing projects, wrap around simple quotes</text><text class="terminal-243650528-r1" x="976" y="825.2" textLength="12.2" clip-path="url(#terminal-243650528-line-33)"> -</text><text class="terminal-243650528-r1" x="0" y="849.6" textLength="353.8" clip-path="url(#terminal-243650528-line-34)">  --bump-message BUMP_MESSAGE</text><text class="terminal-243650528-r1" x="976" y="849.6" textLength="12.2" clip-path="url(#terminal-243650528-line-34)"> -</text><text class="terminal-243650528-r1" x="0" y="874" textLength="902.8" clip-path="url(#terminal-243650528-line-35)">                        template used to create the release commit, useful</text><text class="terminal-243650528-r1" x="976" y="874" textLength="12.2" clip-path="url(#terminal-243650528-line-35)"> -</text><text class="terminal-243650528-r1" x="0" y="898.4" textLength="536.8" clip-path="url(#terminal-243650528-line-36)">                        when working with CI</text><text class="terminal-243650528-r1" x="976" y="898.4" textLength="12.2" clip-path="url(#terminal-243650528-line-36)"> -</text><text class="terminal-243650528-r1" x="0" y="922.8" textLength="244" clip-path="url(#terminal-243650528-line-37)">  --prerelease, -pr </text><text class="terminal-243650528-r2" x="244" y="922.8" textLength="12.2" clip-path="url(#terminal-243650528-line-37)">{</text><text class="terminal-243650528-r1" x="256.2" y="922.8" textLength="158.6" clip-path="url(#terminal-243650528-line-37)">alpha,beta,rc</text><text class="terminal-243650528-r2" x="414.8" y="922.8" textLength="12.2" clip-path="url(#terminal-243650528-line-37)">}</text><text class="terminal-243650528-r1" x="976" y="922.8" textLength="12.2" clip-path="url(#terminal-243650528-line-37)"> -</text><text class="terminal-243650528-r1" x="0" y="947.2" textLength="597.8" clip-path="url(#terminal-243650528-line-38)">                        choose type of prerelease</text><text class="terminal-243650528-r1" x="976" y="947.2" textLength="12.2" clip-path="url(#terminal-243650528-line-38)"> -</text><text class="terminal-243650528-r1" x="0" y="971.6" textLength="353.8" clip-path="url(#terminal-243650528-line-39)">  --devrelease, -d DEVRELEASE</text><text class="terminal-243650528-r1" x="976" y="971.6" textLength="12.2" clip-path="url(#terminal-243650528-line-39)"> -</text><text class="terminal-243650528-r1" x="0" y="996" textLength="841.8" clip-path="url(#terminal-243650528-line-40)">                        specify non-negative integer for dev. release</text><text class="terminal-243650528-r1" x="976" y="996" textLength="12.2" clip-path="url(#terminal-243650528-line-40)"> -</text><text class="terminal-243650528-r1" x="0" y="1020.4" textLength="170.8" clip-path="url(#terminal-243650528-line-41)">  --increment </text><text class="terminal-243650528-r2" x="170.8" y="1020.4" textLength="12.2" clip-path="url(#terminal-243650528-line-41)">{</text><text class="terminal-243650528-r1" x="183" y="1020.4" textLength="207.4" clip-path="url(#terminal-243650528-line-41)">MAJOR,MINOR,PATCH</text><text class="terminal-243650528-r2" x="390.4" y="1020.4" textLength="12.2" clip-path="url(#terminal-243650528-line-41)">}</text><text class="terminal-243650528-r1" x="976" y="1020.4" textLength="12.2" clip-path="url(#terminal-243650528-line-41)"> -</text><text class="terminal-243650528-r1" x="0" y="1044.8" textLength="756.4" clip-path="url(#terminal-243650528-line-42)">                        manually specify the desired increment</text><text class="terminal-243650528-r1" x="976" y="1044.8" textLength="12.2" clip-path="url(#terminal-243650528-line-42)"> -</text><text class="terminal-243650528-r1" x="0" y="1069.2" textLength="231.8" clip-path="url(#terminal-243650528-line-43)">  --increment-mode </text><text class="terminal-243650528-r2" x="231.8" y="1069.2" textLength="12.2" clip-path="url(#terminal-243650528-line-43)">{</text><text class="terminal-243650528-r1" x="244" y="1069.2" textLength="146.4" clip-path="url(#terminal-243650528-line-43)">linear,exact</text><text class="terminal-243650528-r2" x="390.4" y="1069.2" textLength="12.2" clip-path="url(#terminal-243650528-line-43)">}</text><text class="terminal-243650528-r1" x="976" y="1069.2" textLength="12.2" clip-path="url(#terminal-243650528-line-43)"> -</text><text class="terminal-243650528-r1" x="0" y="1093.6" textLength="902.8" clip-path="url(#terminal-243650528-line-44)">                        set the method by which the new version is chosen.</text><text class="terminal-243650528-r1" x="976" y="1093.6" textLength="12.2" clip-path="url(#terminal-243650528-line-44)"> -</text><text class="terminal-243650528-r4" x="292.8" y="1118" textLength="97.6" clip-path="url(#terminal-243650528-line-45)">'linear'</text><text class="terminal-243650528-r2" x="402.6" y="1118" textLength="12.2" clip-path="url(#terminal-243650528-line-45)">(</text><text class="terminal-243650528-r1" x="414.8" y="1118" textLength="85.4" clip-path="url(#terminal-243650528-line-45)">default</text><text class="terminal-243650528-r2" x="500.2" y="1118" textLength="12.2" clip-path="url(#terminal-243650528-line-45)">)</text><text class="terminal-243650528-r1" x="512.4" y="1118" textLength="414.8" clip-path="url(#terminal-243650528-line-45)"> guesses the next version based on</text><text class="terminal-243650528-r1" x="976" y="1118" textLength="12.2" clip-path="url(#terminal-243650528-line-45)"> -</text><text class="terminal-243650528-r1" x="0" y="1142.4" textLength="939.4" clip-path="url(#terminal-243650528-line-46)">                        typical linear version progression, such that bumping</text><text class="terminal-243650528-r1" x="976" y="1142.4" textLength="12.2" clip-path="url(#terminal-243650528-line-46)"> -</text><text class="terminal-243650528-r1" x="0" y="1166.8" textLength="866.2" clip-path="url(#terminal-243650528-line-47)">                        of a pre-release with lower precedence than the</text><text class="terminal-243650528-r1" x="976" y="1166.8" textLength="12.2" clip-path="url(#terminal-243650528-line-47)"> -</text><text class="terminal-243650528-r1" x="0" y="1191.2" textLength="939.4" clip-path="url(#terminal-243650528-line-48)">                        current pre-release phase maintains the current phase</text><text class="terminal-243650528-r1" x="976" y="1191.2" textLength="12.2" clip-path="url(#terminal-243650528-line-48)"> -</text><text class="terminal-243650528-r1" x="0" y="1215.6" textLength="561.2" clip-path="url(#terminal-243650528-line-49)">                        of higher precedence. </text><text class="terminal-243650528-r4" x="561.2" y="1215.6" textLength="85.4" clip-path="url(#terminal-243650528-line-49)">'exact'</text><text class="terminal-243650528-r1" x="646.6" y="1215.6" textLength="305" clip-path="url(#terminal-243650528-line-49)"> applies the changes that</text><text class="terminal-243650528-r1" x="976" y="1215.6" textLength="12.2" clip-path="url(#terminal-243650528-line-49)"> -</text><text class="terminal-243650528-r1" x="0" y="1240" textLength="536.8" clip-path="url(#terminal-243650528-line-50)">                        have been specified </text><text class="terminal-243650528-r2" x="536.8" y="1240" textLength="12.2" clip-path="url(#terminal-243650528-line-50)">(</text><text class="terminal-243650528-r1" x="549" y="1240" textLength="353.8" clip-path="url(#terminal-243650528-line-50)">or determined from the commit</text><text class="terminal-243650528-r1" x="976" y="1240" textLength="12.2" clip-path="url(#terminal-243650528-line-50)"> -</text><text class="terminal-243650528-r1" x="0" y="1264.4" textLength="329.4" clip-path="url(#terminal-243650528-line-51)">                        log</text><text class="terminal-243650528-r2" x="329.4" y="1264.4" textLength="12.2" clip-path="url(#terminal-243650528-line-51)">)</text><text class="terminal-243650528-r1" x="341.6" y="1264.4" textLength="585.6" clip-path="url(#terminal-243650528-line-51)"> without interpretation, such that the increment</text><text class="terminal-243650528-r1" x="976" y="1264.4" textLength="12.2" clip-path="url(#terminal-243650528-line-51)"> -</text><text class="terminal-243650528-r1" x="0" y="1288.8" textLength="707.6" clip-path="url(#terminal-243650528-line-52)">                        and pre-release are always honored</text><text class="terminal-243650528-r1" x="976" y="1288.8" textLength="12.2" clip-path="url(#terminal-243650528-line-52)"> -</text><text class="terminal-243650528-r1" x="0" y="1313.2" textLength="317.2" clip-path="url(#terminal-243650528-line-53)">  --check-consistency, -cc</text><text class="terminal-243650528-r1" x="976" y="1313.2" textLength="12.2" clip-path="url(#terminal-243650528-line-53)"> -</text><text class="terminal-243650528-r1" x="0" y="1337.6" textLength="951.6" clip-path="url(#terminal-243650528-line-54)">                        check consistency among versions defined in commitizen</text><text class="terminal-243650528-r1" x="976" y="1337.6" textLength="12.2" clip-path="url(#terminal-243650528-line-54)"> -</text><text class="terminal-243650528-r1" x="0" y="1362" textLength="671" clip-path="url(#terminal-243650528-line-55)">                        configuration and version_files</text><text class="terminal-243650528-r1" x="976" y="1362" textLength="12.2" clip-path="url(#terminal-243650528-line-55)"> -</text><text class="terminal-243650528-r1" x="0" y="1386.4" textLength="866.2" clip-path="url(#terminal-243650528-line-56)">  --annotated-tag, -at  create annotated tag instead of lightweight one</text><text class="terminal-243650528-r1" x="976" y="1386.4" textLength="12.2" clip-path="url(#terminal-243650528-line-56)"> -</text><text class="terminal-243650528-r1" x="0" y="1410.8" textLength="646.6" clip-path="url(#terminal-243650528-line-57)">  --annotated-tag-message, -atm ANNOTATED_TAG_MESSAGE</text><text class="terminal-243650528-r1" x="976" y="1410.8" textLength="12.2" clip-path="url(#terminal-243650528-line-57)"> -</text><text class="terminal-243650528-r1" x="0" y="1435.2" textLength="634.4" clip-path="url(#terminal-243650528-line-58)">                        create annotated tag message</text><text class="terminal-243650528-r1" x="976" y="1435.2" textLength="12.2" clip-path="url(#terminal-243650528-line-58)"> -</text><text class="terminal-243650528-r1" x="0" y="1459.6" textLength="719.8" clip-path="url(#terminal-243650528-line-59)">  --gpg-sign, -s        sign tag instead of lightweight one</text><text class="terminal-243650528-r1" x="976" y="1459.6" textLength="12.2" clip-path="url(#terminal-243650528-line-59)"> -</text><text class="terminal-243650528-r1" x="0" y="1484" textLength="280.6" clip-path="url(#terminal-243650528-line-60)">  --changelog-to-stdout</text><text class="terminal-243650528-r1" x="976" y="1484" textLength="12.2" clip-path="url(#terminal-243650528-line-60)"> -</text><text class="terminal-243650528-r1" x="0" y="1508.4" textLength="658.8" clip-path="url(#terminal-243650528-line-61)">                        Output changelog to the stdout</text><text class="terminal-243650528-r1" x="976" y="1508.4" textLength="12.2" clip-path="url(#terminal-243650528-line-61)"> -</text><text class="terminal-243650528-r1" x="0" y="1532.8" textLength="292.8" clip-path="url(#terminal-243650528-line-62)">  --git-output-to-stderr</text><text class="terminal-243650528-r1" x="976" y="1532.8" textLength="12.2" clip-path="url(#terminal-243650528-line-62)"> -</text><text class="terminal-243650528-r1" x="0" y="1557.2" textLength="646.6" clip-path="url(#terminal-243650528-line-63)">                        Redirect git output to stderr</text><text class="terminal-243650528-r1" x="976" y="1557.2" textLength="12.2" clip-path="url(#terminal-243650528-line-63)"> -</text><text class="terminal-243650528-r1" x="0" y="1581.6" textLength="744.2" clip-path="url(#terminal-243650528-line-64)">  --retry               retry commit if it fails the 1st time</text><text class="terminal-243650528-r1" x="976" y="1581.6" textLength="12.2" clip-path="url(#terminal-243650528-line-64)"> -</text><text class="terminal-243650528-r1" x="0" y="1606" textLength="939.4" clip-path="url(#terminal-243650528-line-65)">  --major-version-zero  keep major version at zero, even for breaking changes</text><text class="terminal-243650528-r1" x="976" y="1606" textLength="12.2" clip-path="url(#terminal-243650528-line-65)"> -</text><text class="terminal-243650528-r1" x="0" y="1630.4" textLength="305" clip-path="url(#terminal-243650528-line-66)">  --template, -t TEMPLATE</text><text class="terminal-243650528-r1" x="976" y="1630.4" textLength="12.2" clip-path="url(#terminal-243650528-line-66)"> -</text><text class="terminal-243650528-r1" x="0" y="1654.8" textLength="646.6" clip-path="url(#terminal-243650528-line-67)">                        changelog template file name </text><text class="terminal-243650528-r2" x="646.6" y="1654.8" textLength="12.2" clip-path="url(#terminal-243650528-line-67)">(</text><text class="terminal-243650528-r1" x="658.8" y="1654.8" textLength="280.6" clip-path="url(#terminal-243650528-line-67)">relative to the current</text><text class="terminal-243650528-r1" x="976" y="1654.8" textLength="12.2" clip-path="url(#terminal-243650528-line-67)"> -</text><text class="terminal-243650528-r1" x="0" y="1679.2" textLength="500.2" clip-path="url(#terminal-243650528-line-68)">                        working directory</text><text class="terminal-243650528-r2" x="500.2" y="1679.2" textLength="12.2" clip-path="url(#terminal-243650528-line-68)">)</text><text class="terminal-243650528-r1" x="976" y="1679.2" textLength="12.2" clip-path="url(#terminal-243650528-line-68)"> -</text><text class="terminal-243650528-r1" x="0" y="1703.6" textLength="622.2" clip-path="url(#terminal-243650528-line-69)">  --extra, -e EXTRA     a changelog extra variable </text><text class="terminal-243650528-r2" x="622.2" y="1703.6" textLength="12.2" clip-path="url(#terminal-243650528-line-69)">(</text><text class="terminal-243650528-r1" x="634.4" y="1703.6" textLength="146.4" clip-path="url(#terminal-243650528-line-69)">in the form </text><text class="terminal-243650528-r4" x="780.8" y="1703.6" textLength="12.2" clip-path="url(#terminal-243650528-line-69)">'</text><text class="terminal-243650528-r4" x="793" y="1703.6" textLength="36.6" clip-path="url(#terminal-243650528-line-69)">key</text><text class="terminal-243650528-r4" x="829.6" y="1703.6" textLength="12.2" clip-path="url(#terminal-243650528-line-69)">=</text><text class="terminal-243650528-r4" x="841.8" y="1703.6" textLength="61" clip-path="url(#terminal-243650528-line-69)">value</text><text class="terminal-243650528-r4" x="902.8" y="1703.6" textLength="12.2" clip-path="url(#terminal-243650528-line-69)">'</text><text class="terminal-243650528-r2" x="915" y="1703.6" textLength="12.2" clip-path="url(#terminal-243650528-line-69)">)</text><text class="terminal-243650528-r1" x="976" y="1703.6" textLength="12.2" clip-path="url(#terminal-243650528-line-69)"> -</text><text class="terminal-243650528-r1" x="0" y="1728" textLength="280.6" clip-path="url(#terminal-243650528-line-70)">  --file-name FILE_NAME</text><text class="terminal-243650528-r1" x="976" y="1728" textLength="12.2" clip-path="url(#terminal-243650528-line-70)"> -</text><text class="terminal-243650528-r1" x="0" y="1752.4" textLength="573.4" clip-path="url(#terminal-243650528-line-71)">                        file name of changelog </text><text class="terminal-243650528-r2" x="573.4" y="1752.4" textLength="12.2" clip-path="url(#terminal-243650528-line-71)">(</text><text class="terminal-243650528-r1" x="585.6" y="1752.4" textLength="109.8" clip-path="url(#terminal-243650528-line-71)">default: </text><text class="terminal-243650528-r4" x="695.4" y="1752.4" textLength="170.8" clip-path="url(#terminal-243650528-line-71)">'CHANGELOG.md'</text><text class="terminal-243650528-r2" x="866.2" y="1752.4" textLength="12.2" clip-path="url(#terminal-243650528-line-71)">)</text><text class="terminal-243650528-r1" x="976" y="1752.4" textLength="12.2" clip-path="url(#terminal-243650528-line-71)"> -</text><text class="terminal-243650528-r1" x="0" y="1776.8" textLength="475.8" clip-path="url(#terminal-243650528-line-72)">  --prerelease-offset PRERELEASE_OFFSET</text><text class="terminal-243650528-r1" x="976" y="1776.8" textLength="12.2" clip-path="url(#terminal-243650528-line-72)"> -</text><text class="terminal-243650528-r1" x="0" y="1801.2" textLength="719.8" clip-path="url(#terminal-243650528-line-73)">                        start pre-releases with this offset</text><text class="terminal-243650528-r1" x="976" y="1801.2" textLength="12.2" clip-path="url(#terminal-243650528-line-73)"> -</text><text class="terminal-243650528-r1" x="0" y="1825.6" textLength="231.8" clip-path="url(#terminal-243650528-line-74)">  --version-scheme </text><text class="terminal-243650528-r2" x="231.8" y="1825.6" textLength="12.2" clip-path="url(#terminal-243650528-line-74)">{</text><text class="terminal-243650528-r1" x="244" y="1825.6" textLength="256.2" clip-path="url(#terminal-243650528-line-74)">pep440,semver,semver2</text><text class="terminal-243650528-r2" x="500.2" y="1825.6" textLength="12.2" clip-path="url(#terminal-243650528-line-74)">}</text><text class="terminal-243650528-r1" x="976" y="1825.6" textLength="12.2" clip-path="url(#terminal-243650528-line-74)"> -</text><text class="terminal-243650528-r1" x="0" y="1850" textLength="549" clip-path="url(#terminal-243650528-line-75)">                        choose version scheme</text><text class="terminal-243650528-r1" x="976" y="1850" textLength="12.2" clip-path="url(#terminal-243650528-line-75)"> -</text><text class="terminal-243650528-r1" x="0" y="1874.4" textLength="207.4" clip-path="url(#terminal-243650528-line-76)">  --version-type </text><text class="terminal-243650528-r2" x="207.4" y="1874.4" textLength="12.2" clip-path="url(#terminal-243650528-line-76)">{</text><text class="terminal-243650528-r1" x="219.6" y="1874.4" textLength="256.2" clip-path="url(#terminal-243650528-line-76)">pep440,semver,semver2</text><text class="terminal-243650528-r2" x="475.8" y="1874.4" textLength="12.2" clip-path="url(#terminal-243650528-line-76)">}</text><text class="terminal-243650528-r1" x="976" y="1874.4" textLength="12.2" clip-path="url(#terminal-243650528-line-76)"> -</text><text class="terminal-243650528-r1" x="0" y="1898.8" textLength="683.2" clip-path="url(#terminal-243650528-line-77)">                        Deprecated, use --version-scheme</text><text class="terminal-243650528-r1" x="976" y="1898.8" textLength="12.2" clip-path="url(#terminal-243650528-line-77)"> -</text><text class="terminal-243650528-r1" x="0" y="1923.2" textLength="402.6" clip-path="url(#terminal-243650528-line-78)">  --build-metadata BUILD_METADATA</text><text class="terminal-243650528-r1" x="976" y="1923.2" textLength="12.2" clip-path="url(#terminal-243650528-line-78)"> -</text><text class="terminal-243650528-r1" x="0" y="1947.6" textLength="915" clip-path="url(#terminal-243650528-line-79)">                        Add additional build-metadata to the version-number</text><text class="terminal-243650528-r1" x="976" y="1947.6" textLength="12.2" clip-path="url(#terminal-243650528-line-79)"> -</text><text class="terminal-243650528-r1" x="0" y="1972" textLength="854" clip-path="url(#terminal-243650528-line-80)">  --get-next            Determine the next version and write to stdout</text><text class="terminal-243650528-r1" x="976" y="1972" textLength="12.2" clip-path="url(#terminal-243650528-line-80)"> -</text><text class="terminal-243650528-r1" x="0" y="1996.4" textLength="744.2" clip-path="url(#terminal-243650528-line-81)">  --allow-no-commit     bump version without eligible commits</text><text class="terminal-243650528-r1" x="976" y="1996.4" textLength="12.2" clip-path="url(#terminal-243650528-line-81)"> -</text><text class="terminal-243650528-r1" x="976" y="2020.8" textLength="12.2" clip-path="url(#terminal-243650528-line-82)"> + <g class="terminal-1655558888-matrix"> + <text class="terminal-1655558888-r1" x="0" y="20" textLength="195.2" clip-path="url(#terminal-1655558888-line-0)">$ cz bump --help</text><text class="terminal-1655558888-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1655558888-line-0)"> +</text><text class="terminal-1655558888-r1" x="0" y="44.4" textLength="183" clip-path="url(#terminal-1655558888-line-1)">usage: cz bump </text><text class="terminal-1655558888-r2" x="183" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">[</text><text class="terminal-1655558888-r1" x="195.2" y="44.4" textLength="24.4" clip-path="url(#terminal-1655558888-line-1)">-h</text><text class="terminal-1655558888-r2" x="219.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">]</text><text class="terminal-1655558888-r2" x="244" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">[</text><text class="terminal-1655558888-r1" x="256.2" y="44.4" textLength="109.8" clip-path="url(#terminal-1655558888-line-1)">--dry-run</text><text class="terminal-1655558888-r2" x="366" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">]</text><text class="terminal-1655558888-r2" x="390.4" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">[</text><text class="terminal-1655558888-r1" x="402.6" y="44.4" textLength="146.4" clip-path="url(#terminal-1655558888-line-1)">--files-only</text><text class="terminal-1655558888-r2" x="549" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">]</text><text class="terminal-1655558888-r2" x="573.4" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">[</text><text class="terminal-1655558888-r1" x="585.6" y="44.4" textLength="183" clip-path="url(#terminal-1655558888-line-1)">--local-version</text><text class="terminal-1655558888-r2" x="768.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">]</text><text class="terminal-1655558888-r2" x="793" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">[</text><text class="terminal-1655558888-r1" x="805.2" y="44.4" textLength="134.2" clip-path="url(#terminal-1655558888-line-1)">--changelog</text><text class="terminal-1655558888-r2" x="939.4" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">]</text><text class="terminal-1655558888-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)"> +</text><text class="terminal-1655558888-r2" x="183" y="68.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-2)">[</text><text class="terminal-1655558888-r1" x="195.2" y="68.8" textLength="134.2" clip-path="url(#terminal-1655558888-line-2)">--no-verify</text><text class="terminal-1655558888-r2" x="329.4" y="68.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-2)">]</text><text class="terminal-1655558888-r2" x="353.8" y="68.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-2)">[</text><text class="terminal-1655558888-r1" x="366" y="68.8" textLength="61" clip-path="url(#terminal-1655558888-line-2)">--yes</text><text class="terminal-1655558888-r2" x="427" y="68.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-2)">]</text><text class="terminal-1655558888-r2" x="451.4" y="68.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-2)">[</text><text class="terminal-1655558888-r1" x="463.6" y="68.8" textLength="280.6" clip-path="url(#terminal-1655558888-line-2)">--tag-format TAG_FORMAT</text><text class="terminal-1655558888-r2" x="744.2" y="68.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-2)">]</text><text class="terminal-1655558888-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-2)"> +</text><text class="terminal-1655558888-r2" x="183" y="93.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-3)">[</text><text class="terminal-1655558888-r1" x="195.2" y="93.2" textLength="329.4" clip-path="url(#terminal-1655558888-line-3)">--bump-message BUMP_MESSAGE</text><text class="terminal-1655558888-r2" x="524.6" y="93.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-3)">]</text><text class="terminal-1655558888-r2" x="549" y="93.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-3)">[</text><text class="terminal-1655558888-r1" x="561.2" y="93.2" textLength="158.6" clip-path="url(#terminal-1655558888-line-3)">--prerelease </text><text class="terminal-1655558888-r2" x="719.8" y="93.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-3)">{</text><text class="terminal-1655558888-r1" x="732" y="93.2" textLength="158.6" clip-path="url(#terminal-1655558888-line-3)">alpha,beta,rc</text><text class="terminal-1655558888-r2" x="890.6" y="93.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-3)">}</text><text class="terminal-1655558888-r2" x="902.8" y="93.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-3)">]</text><text class="terminal-1655558888-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-3)"> +</text><text class="terminal-1655558888-r2" x="183" y="117.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-4)">[</text><text class="terminal-1655558888-r1" x="195.2" y="117.6" textLength="280.6" clip-path="url(#terminal-1655558888-line-4)">--devrelease DEVRELEASE</text><text class="terminal-1655558888-r2" x="475.8" y="117.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-4)">]</text><text class="terminal-1655558888-r2" x="500.2" y="117.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-4)">[</text><text class="terminal-1655558888-r1" x="512.4" y="117.6" textLength="146.4" clip-path="url(#terminal-1655558888-line-4)">--increment </text><text class="terminal-1655558888-r2" x="658.8" y="117.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-4)">{</text><text class="terminal-1655558888-r1" x="671" y="117.6" textLength="207.4" clip-path="url(#terminal-1655558888-line-4)">MAJOR,MINOR,PATCH</text><text class="terminal-1655558888-r2" x="878.4" y="117.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-4)">}</text><text class="terminal-1655558888-r2" x="890.6" y="117.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-4)">]</text><text class="terminal-1655558888-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-4)"> +</text><text class="terminal-1655558888-r2" x="183" y="142" textLength="12.2" clip-path="url(#terminal-1655558888-line-5)">[</text><text class="terminal-1655558888-r1" x="195.2" y="142" textLength="207.4" clip-path="url(#terminal-1655558888-line-5)">--increment-mode </text><text class="terminal-1655558888-r2" x="402.6" y="142" textLength="12.2" clip-path="url(#terminal-1655558888-line-5)">{</text><text class="terminal-1655558888-r1" x="414.8" y="142" textLength="146.4" clip-path="url(#terminal-1655558888-line-5)">linear,exact</text><text class="terminal-1655558888-r2" x="561.2" y="142" textLength="12.2" clip-path="url(#terminal-1655558888-line-5)">}</text><text class="terminal-1655558888-r2" x="573.4" y="142" textLength="12.2" clip-path="url(#terminal-1655558888-line-5)">]</text><text class="terminal-1655558888-r2" x="597.8" y="142" textLength="12.2" clip-path="url(#terminal-1655558888-line-5)">[</text><text class="terminal-1655558888-r1" x="610" y="142" textLength="231.8" clip-path="url(#terminal-1655558888-line-5)">--check-consistency</text><text class="terminal-1655558888-r2" x="841.8" y="142" textLength="12.2" clip-path="url(#terminal-1655558888-line-5)">]</text><text class="terminal-1655558888-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1655558888-line-5)"> +</text><text class="terminal-1655558888-r2" x="183" y="166.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-6)">[</text><text class="terminal-1655558888-r1" x="195.2" y="166.4" textLength="183" clip-path="url(#terminal-1655558888-line-6)">--annotated-tag</text><text class="terminal-1655558888-r2" x="378.2" y="166.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-6)">]</text><text class="terminal-1655558888-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-6)"> +</text><text class="terminal-1655558888-r2" x="183" y="190.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-7)">[</text><text class="terminal-1655558888-r1" x="195.2" y="190.8" textLength="549" clip-path="url(#terminal-1655558888-line-7)">--annotated-tag-message ANNOTATED_TAG_MESSAGE</text><text class="terminal-1655558888-r2" x="744.2" y="190.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-7)">]</text><text class="terminal-1655558888-r2" x="768.6" y="190.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-7)">[</text><text class="terminal-1655558888-r1" x="780.8" y="190.8" textLength="122" clip-path="url(#terminal-1655558888-line-7)">--gpg-sign</text><text class="terminal-1655558888-r2" x="902.8" y="190.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-7)">]</text><text class="terminal-1655558888-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-7)"> +</text><text class="terminal-1655558888-r2" x="183" y="215.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-8)">[</text><text class="terminal-1655558888-r1" x="195.2" y="215.2" textLength="256.2" clip-path="url(#terminal-1655558888-line-8)">--changelog-to-stdout</text><text class="terminal-1655558888-r2" x="451.4" y="215.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-8)">]</text><text class="terminal-1655558888-r2" x="475.8" y="215.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-8)">[</text><text class="terminal-1655558888-r1" x="488" y="215.2" textLength="268.4" clip-path="url(#terminal-1655558888-line-8)">--git-output-to-stderr</text><text class="terminal-1655558888-r2" x="756.4" y="215.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-8)">]</text><text class="terminal-1655558888-r2" x="780.8" y="215.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-8)">[</text><text class="terminal-1655558888-r1" x="793" y="215.2" textLength="85.4" clip-path="url(#terminal-1655558888-line-8)">--retry</text><text class="terminal-1655558888-r2" x="878.4" y="215.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-8)">]</text><text class="terminal-1655558888-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-8)"> +</text><text class="terminal-1655558888-r2" x="183" y="239.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-9)">[</text><text class="terminal-1655558888-r1" x="195.2" y="239.6" textLength="244" clip-path="url(#terminal-1655558888-line-9)">--major-version-zero</text><text class="terminal-1655558888-r2" x="439.2" y="239.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-9)">]</text><text class="terminal-1655558888-r2" x="463.6" y="239.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-9)">[</text><text class="terminal-1655558888-r1" x="475.8" y="239.6" textLength="231.8" clip-path="url(#terminal-1655558888-line-9)">--template TEMPLATE</text><text class="terminal-1655558888-r2" x="707.6" y="239.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-9)">]</text><text class="terminal-1655558888-r2" x="732" y="239.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-9)">[</text><text class="terminal-1655558888-r1" x="744.2" y="239.6" textLength="158.6" clip-path="url(#terminal-1655558888-line-9)">--extra EXTRA</text><text class="terminal-1655558888-r2" x="902.8" y="239.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-9)">]</text><text class="terminal-1655558888-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-9)"> +</text><text class="terminal-1655558888-r2" x="183" y="264" textLength="12.2" clip-path="url(#terminal-1655558888-line-10)">[</text><text class="terminal-1655558888-r1" x="195.2" y="264" textLength="256.2" clip-path="url(#terminal-1655558888-line-10)">--file-name FILE_NAME</text><text class="terminal-1655558888-r2" x="451.4" y="264" textLength="12.2" clip-path="url(#terminal-1655558888-line-10)">]</text><text class="terminal-1655558888-r2" x="475.8" y="264" textLength="12.2" clip-path="url(#terminal-1655558888-line-10)">[</text><text class="terminal-1655558888-r1" x="488" y="264" textLength="451.4" clip-path="url(#terminal-1655558888-line-10)">--prerelease-offset PRERELEASE_OFFSET</text><text class="terminal-1655558888-r2" x="939.4" y="264" textLength="12.2" clip-path="url(#terminal-1655558888-line-10)">]</text><text class="terminal-1655558888-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-1655558888-line-10)"> +</text><text class="terminal-1655558888-r2" x="183" y="288.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-11)">[</text><text class="terminal-1655558888-r1" x="195.2" y="288.4" textLength="207.4" clip-path="url(#terminal-1655558888-line-11)">--version-scheme </text><text class="terminal-1655558888-r2" x="402.6" y="288.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-11)">{</text><text class="terminal-1655558888-r1" x="414.8" y="288.4" textLength="256.2" clip-path="url(#terminal-1655558888-line-11)">pep440,semver,semver2</text><text class="terminal-1655558888-r2" x="671" y="288.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-11)">}</text><text class="terminal-1655558888-r2" x="683.2" y="288.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-11)">]</text><text class="terminal-1655558888-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-11)"> +</text><text class="terminal-1655558888-r2" x="183" y="312.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-12)">[</text><text class="terminal-1655558888-r1" x="195.2" y="312.8" textLength="183" clip-path="url(#terminal-1655558888-line-12)">--version-type </text><text class="terminal-1655558888-r2" x="378.2" y="312.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-12)">{</text><text class="terminal-1655558888-r1" x="390.4" y="312.8" textLength="256.2" clip-path="url(#terminal-1655558888-line-12)">pep440,semver,semver2</text><text class="terminal-1655558888-r2" x="646.6" y="312.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-12)">}</text><text class="terminal-1655558888-r2" x="658.8" y="312.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-12)">]</text><text class="terminal-1655558888-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-12)"> +</text><text class="terminal-1655558888-r2" x="183" y="337.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-13)">[</text><text class="terminal-1655558888-r1" x="195.2" y="337.2" textLength="378.2" clip-path="url(#terminal-1655558888-line-13)">--build-metadata BUILD_METADATA</text><text class="terminal-1655558888-r2" x="573.4" y="337.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-13)">]</text><text class="terminal-1655558888-r2" x="597.8" y="337.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-13)">[</text><text class="terminal-1655558888-r1" x="610" y="337.2" textLength="122" clip-path="url(#terminal-1655558888-line-13)">--get-next</text><text class="terminal-1655558888-r2" x="732" y="337.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-13)">]</text><text class="terminal-1655558888-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-13)"> +</text><text class="terminal-1655558888-r2" x="183" y="361.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-14)">[</text><text class="terminal-1655558888-r1" x="195.2" y="361.6" textLength="207.4" clip-path="url(#terminal-1655558888-line-14)">--allow-no-commit</text><text class="terminal-1655558888-r2" x="402.6" y="361.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-14)">]</text><text class="terminal-1655558888-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-14)"> +</text><text class="terminal-1655558888-r2" x="183" y="386" textLength="12.2" clip-path="url(#terminal-1655558888-line-15)">[</text><text class="terminal-1655558888-r1" x="195.2" y="386" textLength="170.8" clip-path="url(#terminal-1655558888-line-15)">MANUAL_VERSION</text><text class="terminal-1655558888-r2" x="366" y="386" textLength="12.2" clip-path="url(#terminal-1655558888-line-15)">]</text><text class="terminal-1655558888-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-1655558888-line-15)"> +</text><text class="terminal-1655558888-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-16)"> +</text><text class="terminal-1655558888-r1" x="0" y="434.8" textLength="512.4" clip-path="url(#terminal-1655558888-line-17)">bump semantic version based on the git log</text><text class="terminal-1655558888-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-17)"> +</text><text class="terminal-1655558888-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-18)"> +</text><text class="terminal-1655558888-r1" x="0" y="483.6" textLength="256.2" clip-path="url(#terminal-1655558888-line-19)">positional arguments:</text><text class="terminal-1655558888-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-19)"> +</text><text class="terminal-1655558888-r1" x="0" y="508" textLength="610" clip-path="url(#terminal-1655558888-line-20)">  MANUAL_VERSION        bump to the given version </text><text class="terminal-1655558888-r2" x="610" y="508" textLength="12.2" clip-path="url(#terminal-1655558888-line-20)">(</text><text class="terminal-1655558888-r1" x="622.2" y="508" textLength="61" clip-path="url(#terminal-1655558888-line-20)">e.g: </text><text class="terminal-1655558888-r3" x="683.2" y="508" textLength="36.6" clip-path="url(#terminal-1655558888-line-20)">1.5</text><text class="terminal-1655558888-r1" x="719.8" y="508" textLength="12.2" clip-path="url(#terminal-1655558888-line-20)">.</text><text class="terminal-1655558888-r3" x="732" y="508" textLength="12.2" clip-path="url(#terminal-1655558888-line-20)">3</text><text class="terminal-1655558888-r2" x="744.2" y="508" textLength="12.2" clip-path="url(#terminal-1655558888-line-20)">)</text><text class="terminal-1655558888-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-1655558888-line-20)"> +</text><text class="terminal-1655558888-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-21)"> +</text><text class="terminal-1655558888-r1" x="0" y="556.8" textLength="97.6" clip-path="url(#terminal-1655558888-line-22)">options:</text><text class="terminal-1655558888-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-22)"> +</text><text class="terminal-1655558888-r1" x="0" y="581.2" textLength="671" clip-path="url(#terminal-1655558888-line-23)">  -h, --help            show this help message and exit</text><text class="terminal-1655558888-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-23)"> +</text><text class="terminal-1655558888-r1" x="0" y="605.6" textLength="915" clip-path="url(#terminal-1655558888-line-24)">  --dry-run             show output to stdout, no commit, no modified files</text><text class="terminal-1655558888-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-24)"> +</text><text class="terminal-1655558888-r1" x="0" y="630" textLength="793" clip-path="url(#terminal-1655558888-line-25)">  --files-only          bump version in the files from the config</text><text class="terminal-1655558888-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-1655558888-line-25)"> +</text><text class="terminal-1655558888-r1" x="0" y="654.4" textLength="719.8" clip-path="url(#terminal-1655558888-line-26)">  --local-version       bump only the local version portion</text><text class="terminal-1655558888-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-26)"> +</text><text class="terminal-1655558888-r1" x="0" y="678.8" textLength="841.8" clip-path="url(#terminal-1655558888-line-27)">  --changelog, -ch      generate the changelog for the newest version</text><text class="terminal-1655558888-r1" x="976" y="678.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-27)"> +</text><text class="terminal-1655558888-r1" x="0" y="703.2" textLength="902.8" clip-path="url(#terminal-1655558888-line-28)">  --no-verify           this option bypasses the pre-commit and commit-msg</text><text class="terminal-1655558888-r1" x="976" y="703.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-28)"> +</text><text class="terminal-1655558888-r1" x="0" y="727.6" textLength="353.8" clip-path="url(#terminal-1655558888-line-29)">                        hooks</text><text class="terminal-1655558888-r1" x="976" y="727.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-29)"> +</text><text class="terminal-1655558888-r1" x="0" y="752" textLength="719.8" clip-path="url(#terminal-1655558888-line-30)">  --yes                 accept automatically questions done</text><text class="terminal-1655558888-r1" x="976" y="752" textLength="12.2" clip-path="url(#terminal-1655558888-line-30)"> +</text><text class="terminal-1655558888-r1" x="0" y="776.4" textLength="305" clip-path="url(#terminal-1655558888-line-31)">  --tag-format TAG_FORMAT</text><text class="terminal-1655558888-r1" x="976" y="776.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-31)"> +</text><text class="terminal-1655558888-r1" x="0" y="800.8" textLength="939.4" clip-path="url(#terminal-1655558888-line-32)">                        the format used to tag the commit and read it, use it</text><text class="terminal-1655558888-r1" x="976" y="800.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-32)"> +</text><text class="terminal-1655558888-r1" x="0" y="825.2" textLength="866.2" clip-path="url(#terminal-1655558888-line-33)">                        in existing projects, wrap around simple quotes</text><text class="terminal-1655558888-r1" x="976" y="825.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-33)"> +</text><text class="terminal-1655558888-r1" x="0" y="849.6" textLength="353.8" clip-path="url(#terminal-1655558888-line-34)">  --bump-message BUMP_MESSAGE</text><text class="terminal-1655558888-r1" x="976" y="849.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-34)"> +</text><text class="terminal-1655558888-r1" x="0" y="874" textLength="902.8" clip-path="url(#terminal-1655558888-line-35)">                        template used to create the release commit, useful</text><text class="terminal-1655558888-r1" x="976" y="874" textLength="12.2" clip-path="url(#terminal-1655558888-line-35)"> +</text><text class="terminal-1655558888-r1" x="0" y="898.4" textLength="536.8" clip-path="url(#terminal-1655558888-line-36)">                        when working with CI</text><text class="terminal-1655558888-r1" x="976" y="898.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-36)"> +</text><text class="terminal-1655558888-r1" x="0" y="922.8" textLength="244" clip-path="url(#terminal-1655558888-line-37)">  --prerelease, -pr </text><text class="terminal-1655558888-r2" x="244" y="922.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-37)">{</text><text class="terminal-1655558888-r1" x="256.2" y="922.8" textLength="158.6" clip-path="url(#terminal-1655558888-line-37)">alpha,beta,rc</text><text class="terminal-1655558888-r2" x="414.8" y="922.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-37)">}</text><text class="terminal-1655558888-r1" x="976" y="922.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-37)"> +</text><text class="terminal-1655558888-r1" x="0" y="947.2" textLength="597.8" clip-path="url(#terminal-1655558888-line-38)">                        choose type of prerelease</text><text class="terminal-1655558888-r1" x="976" y="947.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-38)"> +</text><text class="terminal-1655558888-r1" x="0" y="971.6" textLength="353.8" clip-path="url(#terminal-1655558888-line-39)">  --devrelease, -d DEVRELEASE</text><text class="terminal-1655558888-r1" x="976" y="971.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-39)"> +</text><text class="terminal-1655558888-r1" x="0" y="996" textLength="841.8" clip-path="url(#terminal-1655558888-line-40)">                        specify non-negative integer for dev. release</text><text class="terminal-1655558888-r1" x="976" y="996" textLength="12.2" clip-path="url(#terminal-1655558888-line-40)"> +</text><text class="terminal-1655558888-r1" x="0" y="1020.4" textLength="170.8" clip-path="url(#terminal-1655558888-line-41)">  --increment </text><text class="terminal-1655558888-r2" x="170.8" y="1020.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-41)">{</text><text class="terminal-1655558888-r1" x="183" y="1020.4" textLength="207.4" clip-path="url(#terminal-1655558888-line-41)">MAJOR,MINOR,PATCH</text><text class="terminal-1655558888-r2" x="390.4" y="1020.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-41)">}</text><text class="terminal-1655558888-r1" x="976" y="1020.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-41)"> +</text><text class="terminal-1655558888-r1" x="0" y="1044.8" textLength="756.4" clip-path="url(#terminal-1655558888-line-42)">                        manually specify the desired increment</text><text class="terminal-1655558888-r1" x="976" y="1044.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-42)"> +</text><text class="terminal-1655558888-r1" x="0" y="1069.2" textLength="231.8" clip-path="url(#terminal-1655558888-line-43)">  --increment-mode </text><text class="terminal-1655558888-r2" x="231.8" y="1069.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-43)">{</text><text class="terminal-1655558888-r1" x="244" y="1069.2" textLength="146.4" clip-path="url(#terminal-1655558888-line-43)">linear,exact</text><text class="terminal-1655558888-r2" x="390.4" y="1069.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-43)">}</text><text class="terminal-1655558888-r1" x="976" y="1069.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-43)"> +</text><text class="terminal-1655558888-r1" x="0" y="1093.6" textLength="902.8" clip-path="url(#terminal-1655558888-line-44)">                        set the method by which the new version is chosen.</text><text class="terminal-1655558888-r1" x="976" y="1093.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-44)"> +</text><text class="terminal-1655558888-r4" x="292.8" y="1118" textLength="97.6" clip-path="url(#terminal-1655558888-line-45)">'linear'</text><text class="terminal-1655558888-r2" x="402.6" y="1118" textLength="12.2" clip-path="url(#terminal-1655558888-line-45)">(</text><text class="terminal-1655558888-r1" x="414.8" y="1118" textLength="85.4" clip-path="url(#terminal-1655558888-line-45)">default</text><text class="terminal-1655558888-r2" x="500.2" y="1118" textLength="12.2" clip-path="url(#terminal-1655558888-line-45)">)</text><text class="terminal-1655558888-r1" x="512.4" y="1118" textLength="414.8" clip-path="url(#terminal-1655558888-line-45)"> guesses the next version based on</text><text class="terminal-1655558888-r1" x="976" y="1118" textLength="12.2" clip-path="url(#terminal-1655558888-line-45)"> +</text><text class="terminal-1655558888-r1" x="0" y="1142.4" textLength="939.4" clip-path="url(#terminal-1655558888-line-46)">                        typical linear version progression, such that bumping</text><text class="terminal-1655558888-r1" x="976" y="1142.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-46)"> +</text><text class="terminal-1655558888-r1" x="0" y="1166.8" textLength="866.2" clip-path="url(#terminal-1655558888-line-47)">                        of a pre-release with lower precedence than the</text><text class="terminal-1655558888-r1" x="976" y="1166.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-47)"> +</text><text class="terminal-1655558888-r1" x="0" y="1191.2" textLength="939.4" clip-path="url(#terminal-1655558888-line-48)">                        current pre-release phase maintains the current phase</text><text class="terminal-1655558888-r1" x="976" y="1191.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-48)"> +</text><text class="terminal-1655558888-r1" x="0" y="1215.6" textLength="561.2" clip-path="url(#terminal-1655558888-line-49)">                        of higher precedence. </text><text class="terminal-1655558888-r4" x="561.2" y="1215.6" textLength="85.4" clip-path="url(#terminal-1655558888-line-49)">'exact'</text><text class="terminal-1655558888-r1" x="646.6" y="1215.6" textLength="305" clip-path="url(#terminal-1655558888-line-49)"> applies the changes that</text><text class="terminal-1655558888-r1" x="976" y="1215.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-49)"> +</text><text class="terminal-1655558888-r1" x="0" y="1240" textLength="536.8" clip-path="url(#terminal-1655558888-line-50)">                        have been specified </text><text class="terminal-1655558888-r2" x="536.8" y="1240" textLength="12.2" clip-path="url(#terminal-1655558888-line-50)">(</text><text class="terminal-1655558888-r1" x="549" y="1240" textLength="353.8" clip-path="url(#terminal-1655558888-line-50)">or determined from the commit</text><text class="terminal-1655558888-r1" x="976" y="1240" textLength="12.2" clip-path="url(#terminal-1655558888-line-50)"> +</text><text class="terminal-1655558888-r1" x="0" y="1264.4" textLength="329.4" clip-path="url(#terminal-1655558888-line-51)">                        log</text><text class="terminal-1655558888-r2" x="329.4" y="1264.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-51)">)</text><text class="terminal-1655558888-r1" x="341.6" y="1264.4" textLength="585.6" clip-path="url(#terminal-1655558888-line-51)"> without interpretation, such that the increment</text><text class="terminal-1655558888-r1" x="976" y="1264.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-51)"> +</text><text class="terminal-1655558888-r1" x="0" y="1288.8" textLength="707.6" clip-path="url(#terminal-1655558888-line-52)">                        and pre-release are always honored</text><text class="terminal-1655558888-r1" x="976" y="1288.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-52)"> +</text><text class="terminal-1655558888-r1" x="0" y="1313.2" textLength="317.2" clip-path="url(#terminal-1655558888-line-53)">  --check-consistency, -cc</text><text class="terminal-1655558888-r1" x="976" y="1313.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-53)"> +</text><text class="terminal-1655558888-r1" x="0" y="1337.6" textLength="951.6" clip-path="url(#terminal-1655558888-line-54)">                        check consistency among versions defined in commitizen</text><text class="terminal-1655558888-r1" x="976" y="1337.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-54)"> +</text><text class="terminal-1655558888-r1" x="0" y="1362" textLength="671" clip-path="url(#terminal-1655558888-line-55)">                        configuration and version_files</text><text class="terminal-1655558888-r1" x="976" y="1362" textLength="12.2" clip-path="url(#terminal-1655558888-line-55)"> +</text><text class="terminal-1655558888-r1" x="0" y="1386.4" textLength="866.2" clip-path="url(#terminal-1655558888-line-56)">  --annotated-tag, -at  create annotated tag instead of lightweight one</text><text class="terminal-1655558888-r1" x="976" y="1386.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-56)"> +</text><text class="terminal-1655558888-r1" x="0" y="1410.8" textLength="646.6" clip-path="url(#terminal-1655558888-line-57)">  --annotated-tag-message, -atm ANNOTATED_TAG_MESSAGE</text><text class="terminal-1655558888-r1" x="976" y="1410.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-57)"> +</text><text class="terminal-1655558888-r1" x="0" y="1435.2" textLength="634.4" clip-path="url(#terminal-1655558888-line-58)">                        create annotated tag message</text><text class="terminal-1655558888-r1" x="976" y="1435.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-58)"> +</text><text class="terminal-1655558888-r1" x="0" y="1459.6" textLength="719.8" clip-path="url(#terminal-1655558888-line-59)">  --gpg-sign, -s        sign tag instead of lightweight one</text><text class="terminal-1655558888-r1" x="976" y="1459.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-59)"> +</text><text class="terminal-1655558888-r1" x="0" y="1484" textLength="280.6" clip-path="url(#terminal-1655558888-line-60)">  --changelog-to-stdout</text><text class="terminal-1655558888-r1" x="976" y="1484" textLength="12.2" clip-path="url(#terminal-1655558888-line-60)"> +</text><text class="terminal-1655558888-r1" x="0" y="1508.4" textLength="658.8" clip-path="url(#terminal-1655558888-line-61)">                        Output changelog to the stdout</text><text class="terminal-1655558888-r1" x="976" y="1508.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-61)"> +</text><text class="terminal-1655558888-r1" x="0" y="1532.8" textLength="292.8" clip-path="url(#terminal-1655558888-line-62)">  --git-output-to-stderr</text><text class="terminal-1655558888-r1" x="976" y="1532.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-62)"> +</text><text class="terminal-1655558888-r1" x="0" y="1557.2" textLength="646.6" clip-path="url(#terminal-1655558888-line-63)">                        Redirect git output to stderr</text><text class="terminal-1655558888-r1" x="976" y="1557.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-63)"> +</text><text class="terminal-1655558888-r1" x="0" y="1581.6" textLength="744.2" clip-path="url(#terminal-1655558888-line-64)">  --retry               retry commit if it fails the 1st time</text><text class="terminal-1655558888-r1" x="976" y="1581.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-64)"> +</text><text class="terminal-1655558888-r1" x="0" y="1606" textLength="939.4" clip-path="url(#terminal-1655558888-line-65)">  --major-version-zero  keep major version at zero, even for breaking changes</text><text class="terminal-1655558888-r1" x="976" y="1606" textLength="12.2" clip-path="url(#terminal-1655558888-line-65)"> +</text><text class="terminal-1655558888-r1" x="0" y="1630.4" textLength="305" clip-path="url(#terminal-1655558888-line-66)">  --template, -t TEMPLATE</text><text class="terminal-1655558888-r1" x="976" y="1630.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-66)"> +</text><text class="terminal-1655558888-r1" x="0" y="1654.8" textLength="646.6" clip-path="url(#terminal-1655558888-line-67)">                        changelog template file name </text><text class="terminal-1655558888-r2" x="646.6" y="1654.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-67)">(</text><text class="terminal-1655558888-r1" x="658.8" y="1654.8" textLength="280.6" clip-path="url(#terminal-1655558888-line-67)">relative to the current</text><text class="terminal-1655558888-r1" x="976" y="1654.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-67)"> +</text><text class="terminal-1655558888-r1" x="0" y="1679.2" textLength="500.2" clip-path="url(#terminal-1655558888-line-68)">                        working directory</text><text class="terminal-1655558888-r2" x="500.2" y="1679.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-68)">)</text><text class="terminal-1655558888-r1" x="976" y="1679.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-68)"> +</text><text class="terminal-1655558888-r1" x="0" y="1703.6" textLength="622.2" clip-path="url(#terminal-1655558888-line-69)">  --extra, -e EXTRA     a changelog extra variable </text><text class="terminal-1655558888-r2" x="622.2" y="1703.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-69)">(</text><text class="terminal-1655558888-r1" x="634.4" y="1703.6" textLength="146.4" clip-path="url(#terminal-1655558888-line-69)">in the form </text><text class="terminal-1655558888-r4" x="780.8" y="1703.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-69)">'</text><text class="terminal-1655558888-r4" x="793" y="1703.6" textLength="36.6" clip-path="url(#terminal-1655558888-line-69)">key</text><text class="terminal-1655558888-r4" x="829.6" y="1703.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-69)">=</text><text class="terminal-1655558888-r4" x="841.8" y="1703.6" textLength="61" clip-path="url(#terminal-1655558888-line-69)">value</text><text class="terminal-1655558888-r4" x="902.8" y="1703.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-69)">'</text><text class="terminal-1655558888-r2" x="915" y="1703.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-69)">)</text><text class="terminal-1655558888-r1" x="976" y="1703.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-69)"> +</text><text class="terminal-1655558888-r1" x="0" y="1728" textLength="280.6" clip-path="url(#terminal-1655558888-line-70)">  --file-name FILE_NAME</text><text class="terminal-1655558888-r1" x="976" y="1728" textLength="12.2" clip-path="url(#terminal-1655558888-line-70)"> +</text><text class="terminal-1655558888-r1" x="0" y="1752.4" textLength="573.4" clip-path="url(#terminal-1655558888-line-71)">                        file name of changelog </text><text class="terminal-1655558888-r2" x="573.4" y="1752.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-71)">(</text><text class="terminal-1655558888-r1" x="585.6" y="1752.4" textLength="109.8" clip-path="url(#terminal-1655558888-line-71)">default: </text><text class="terminal-1655558888-r4" x="695.4" y="1752.4" textLength="170.8" clip-path="url(#terminal-1655558888-line-71)">'CHANGELOG.md'</text><text class="terminal-1655558888-r2" x="866.2" y="1752.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-71)">)</text><text class="terminal-1655558888-r1" x="976" y="1752.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-71)"> +</text><text class="terminal-1655558888-r1" x="0" y="1776.8" textLength="475.8" clip-path="url(#terminal-1655558888-line-72)">  --prerelease-offset PRERELEASE_OFFSET</text><text class="terminal-1655558888-r1" x="976" y="1776.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-72)"> +</text><text class="terminal-1655558888-r1" x="0" y="1801.2" textLength="719.8" clip-path="url(#terminal-1655558888-line-73)">                        start pre-releases with this offset</text><text class="terminal-1655558888-r1" x="976" y="1801.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-73)"> +</text><text class="terminal-1655558888-r1" x="0" y="1825.6" textLength="231.8" clip-path="url(#terminal-1655558888-line-74)">  --version-scheme </text><text class="terminal-1655558888-r2" x="231.8" y="1825.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-74)">{</text><text class="terminal-1655558888-r1" x="244" y="1825.6" textLength="256.2" clip-path="url(#terminal-1655558888-line-74)">pep440,semver,semver2</text><text class="terminal-1655558888-r2" x="500.2" y="1825.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-74)">}</text><text class="terminal-1655558888-r1" x="976" y="1825.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-74)"> +</text><text class="terminal-1655558888-r1" x="0" y="1850" textLength="549" clip-path="url(#terminal-1655558888-line-75)">                        choose version scheme</text><text class="terminal-1655558888-r1" x="976" y="1850" textLength="12.2" clip-path="url(#terminal-1655558888-line-75)"> +</text><text class="terminal-1655558888-r1" x="0" y="1874.4" textLength="207.4" clip-path="url(#terminal-1655558888-line-76)">  --version-type </text><text class="terminal-1655558888-r2" x="207.4" y="1874.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-76)">{</text><text class="terminal-1655558888-r1" x="219.6" y="1874.4" textLength="256.2" clip-path="url(#terminal-1655558888-line-76)">pep440,semver,semver2</text><text class="terminal-1655558888-r2" x="475.8" y="1874.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-76)">}</text><text class="terminal-1655558888-r1" x="976" y="1874.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-76)"> +</text><text class="terminal-1655558888-r1" x="0" y="1898.8" textLength="780.8" clip-path="url(#terminal-1655558888-line-77)">                        Deprecated, use --version-scheme instead</text><text class="terminal-1655558888-r1" x="976" y="1898.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-77)"> +</text><text class="terminal-1655558888-r1" x="0" y="1923.2" textLength="402.6" clip-path="url(#terminal-1655558888-line-78)">  --build-metadata BUILD_METADATA</text><text class="terminal-1655558888-r1" x="976" y="1923.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-78)"> +</text><text class="terminal-1655558888-r1" x="0" y="1947.6" textLength="915" clip-path="url(#terminal-1655558888-line-79)">                        Add additional build-metadata to the version-number</text><text class="terminal-1655558888-r1" x="976" y="1947.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-79)"> +</text><text class="terminal-1655558888-r1" x="0" y="1972" textLength="854" clip-path="url(#terminal-1655558888-line-80)">  --get-next            Determine the next version and write to stdout</text><text class="terminal-1655558888-r1" x="976" y="1972" textLength="12.2" clip-path="url(#terminal-1655558888-line-80)"> +</text><text class="terminal-1655558888-r1" x="0" y="1996.4" textLength="744.2" clip-path="url(#terminal-1655558888-line-81)">  --allow-no-commit     bump version without eligible commits</text><text class="terminal-1655558888-r1" x="976" y="1996.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-81)"> +</text><text class="terminal-1655558888-r1" x="976" y="2020.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-82)"> </text> </g> </g> 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 @@ -<svg class="rich-terminal" viewBox="0 0 994 1050.4" xmlns="http://www.w3.org/2000/svg"> +<svg class="rich-terminal" viewBox="0 0 994 1099.2" xmlns="http://www.w3.org/2000/svg"> <!-- Generated with Rich https://www.textualize.io --> <style> @@ -19,202 +19,210 @@ font-weight: 700; } - .terminal-1106739011-matrix { + .terminal-2926696453-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1106739011-title { + .terminal-2926696453-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1106739011-r1 { fill: #c5c8c6 } -.terminal-1106739011-r2 { fill: #c5c8c6;font-weight: bold } -.terminal-1106739011-r3 { fill: #68a0b3;font-weight: bold } -.terminal-1106739011-r4 { fill: #98a84b } + .terminal-2926696453-r1 { fill: #c5c8c6 } +.terminal-2926696453-r2 { fill: #c5c8c6;font-weight: bold } +.terminal-2926696453-r3 { fill: #68a0b3;font-weight: bold } +.terminal-2926696453-r4 { fill: #98a84b } </style> <defs> - <clipPath id="terminal-1106739011-clip-terminal"> - <rect x="0" y="0" width="975.0" height="999.4" /> + <clipPath id="terminal-2926696453-clip-terminal"> + <rect x="0" y="0" width="975.0" height="1048.2" /> </clipPath> - <clipPath id="terminal-1106739011-line-0"> + <clipPath id="terminal-2926696453-line-0"> <rect x="0" y="1.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-1"> +<clipPath id="terminal-2926696453-line-1"> <rect x="0" y="25.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-2"> +<clipPath id="terminal-2926696453-line-2"> <rect x="0" y="50.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-3"> +<clipPath id="terminal-2926696453-line-3"> <rect x="0" y="74.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-4"> +<clipPath id="terminal-2926696453-line-4"> <rect x="0" y="99.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-5"> +<clipPath id="terminal-2926696453-line-5"> <rect x="0" y="123.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-6"> +<clipPath id="terminal-2926696453-line-6"> <rect x="0" y="147.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-7"> +<clipPath id="terminal-2926696453-line-7"> <rect x="0" y="172.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-8"> +<clipPath id="terminal-2926696453-line-8"> <rect x="0" y="196.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-9"> +<clipPath id="terminal-2926696453-line-9"> <rect x="0" y="221.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-10"> +<clipPath id="terminal-2926696453-line-10"> <rect x="0" y="245.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-11"> +<clipPath id="terminal-2926696453-line-11"> <rect x="0" y="269.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-12"> +<clipPath id="terminal-2926696453-line-12"> <rect x="0" y="294.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-13"> +<clipPath id="terminal-2926696453-line-13"> <rect x="0" y="318.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-14"> +<clipPath id="terminal-2926696453-line-14"> <rect x="0" y="343.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-15"> +<clipPath id="terminal-2926696453-line-15"> <rect x="0" y="367.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-16"> +<clipPath id="terminal-2926696453-line-16"> <rect x="0" y="391.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-17"> +<clipPath id="terminal-2926696453-line-17"> <rect x="0" y="416.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-18"> +<clipPath id="terminal-2926696453-line-18"> <rect x="0" y="440.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-19"> +<clipPath id="terminal-2926696453-line-19"> <rect x="0" y="465.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-20"> +<clipPath id="terminal-2926696453-line-20"> <rect x="0" y="489.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-21"> +<clipPath id="terminal-2926696453-line-21"> <rect x="0" y="513.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-22"> +<clipPath id="terminal-2926696453-line-22"> <rect x="0" y="538.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-23"> +<clipPath id="terminal-2926696453-line-23"> <rect x="0" y="562.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-24"> +<clipPath id="terminal-2926696453-line-24"> <rect x="0" y="587.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-25"> +<clipPath id="terminal-2926696453-line-25"> <rect x="0" y="611.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-26"> +<clipPath id="terminal-2926696453-line-26"> <rect x="0" y="635.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-27"> +<clipPath id="terminal-2926696453-line-27"> <rect x="0" y="660.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-28"> +<clipPath id="terminal-2926696453-line-28"> <rect x="0" y="684.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-29"> +<clipPath id="terminal-2926696453-line-29"> <rect x="0" y="709.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-30"> +<clipPath id="terminal-2926696453-line-30"> <rect x="0" y="733.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-31"> +<clipPath id="terminal-2926696453-line-31"> <rect x="0" y="757.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-32"> +<clipPath id="terminal-2926696453-line-32"> <rect x="0" y="782.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-33"> +<clipPath id="terminal-2926696453-line-33"> <rect x="0" y="806.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-34"> +<clipPath id="terminal-2926696453-line-34"> <rect x="0" y="831.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-35"> +<clipPath id="terminal-2926696453-line-35"> <rect x="0" y="855.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-36"> +<clipPath id="terminal-2926696453-line-36"> <rect x="0" y="879.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-37"> +<clipPath id="terminal-2926696453-line-37"> <rect x="0" y="904.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-38"> +<clipPath id="terminal-2926696453-line-38"> <rect x="0" y="928.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1106739011-line-39"> +<clipPath id="terminal-2926696453-line-39"> <rect x="0" y="953.1" width="976" height="24.65"/> </clipPath> +<clipPath id="terminal-2926696453-line-40"> + <rect x="0" y="977.5" width="976" height="24.65"/> + </clipPath> +<clipPath id="terminal-2926696453-line-41"> + <rect x="0" y="1001.9" width="976" height="24.65"/> + </clipPath> </defs> - <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="1048.4" rx="8"/> + <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="1097.2" rx="8"/> <g transform="translate(26,22)"> <circle cx="0" cy="0" r="7" fill="#ff5f57"/> <circle cx="22" cy="0" r="7" fill="#febc2e"/> <circle cx="44" cy="0" r="7" fill="#28c840"/> </g> - <g transform="translate(9, 41)" clip-path="url(#terminal-1106739011-clip-terminal)"> + <g transform="translate(9, 41)" clip-path="url(#terminal-2926696453-clip-terminal)"> - <g class="terminal-1106739011-matrix"> - <text class="terminal-1106739011-r1" x="0" y="20" textLength="256.2" clip-path="url(#terminal-1106739011-line-0)">$ cz changelog --help</text><text class="terminal-1106739011-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1106739011-line-0)"> -</text><text class="terminal-1106739011-r1" x="0" y="44.4" textLength="244" clip-path="url(#terminal-1106739011-line-1)">usage: cz changelog </text><text class="terminal-1106739011-r2" x="244" y="44.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-1)">[</text><text class="terminal-1106739011-r1" x="256.2" y="44.4" textLength="24.4" clip-path="url(#terminal-1106739011-line-1)">-h</text><text class="terminal-1106739011-r2" x="280.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-1)">]</text><text class="terminal-1106739011-r2" x="305" y="44.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-1)">[</text><text class="terminal-1106739011-r1" x="317.2" y="44.4" textLength="109.8" clip-path="url(#terminal-1106739011-line-1)">--dry-run</text><text class="terminal-1106739011-r2" x="427" y="44.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-1)">]</text><text class="terminal-1106739011-r2" x="451.4" y="44.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-1)">[</text><text class="terminal-1106739011-r1" x="463.6" y="44.4" textLength="256.2" clip-path="url(#terminal-1106739011-line-1)">--file-name FILE_NAME</text><text class="terminal-1106739011-r2" x="719.8" y="44.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-1)">]</text><text class="terminal-1106739011-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-1)"> -</text><text class="terminal-1106739011-r2" x="244" y="68.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-2)">[</text><text class="terminal-1106739011-r1" x="256.2" y="68.8" textLength="475.8" clip-path="url(#terminal-1106739011-line-2)">--unreleased-version UNRELEASED_VERSION</text><text class="terminal-1106739011-r2" x="732" y="68.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-2)">]</text><text class="terminal-1106739011-r2" x="756.4" y="68.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-2)">[</text><text class="terminal-1106739011-r1" x="768.6" y="68.8" textLength="158.6" clip-path="url(#terminal-1106739011-line-2)">--incremental</text><text class="terminal-1106739011-r2" x="927.2" y="68.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-2)">]</text><text class="terminal-1106739011-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-2)"> -</text><text class="terminal-1106739011-r2" x="244" y="93.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-3)">[</text><text class="terminal-1106739011-r1" x="256.2" y="93.2" textLength="256.2" clip-path="url(#terminal-1106739011-line-3)">--start-rev START_REV</text><text class="terminal-1106739011-r2" x="512.4" y="93.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-3)">]</text><text class="terminal-1106739011-r2" x="536.8" y="93.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-3)">[</text><text class="terminal-1106739011-r1" x="549" y="93.2" textLength="219.6" clip-path="url(#terminal-1106739011-line-3)">--merge-prerelease</text><text class="terminal-1106739011-r2" x="768.6" y="93.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-3)">]</text><text class="terminal-1106739011-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-3)"> -</text><text class="terminal-1106739011-r2" x="244" y="117.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-4)">[</text><text class="terminal-1106739011-r1" x="256.2" y="117.6" textLength="207.4" clip-path="url(#terminal-1106739011-line-4)">--version-scheme </text><text class="terminal-1106739011-r2" x="463.6" y="117.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-4)">{</text><text class="terminal-1106739011-r1" x="475.8" y="117.6" textLength="256.2" clip-path="url(#terminal-1106739011-line-4)">pep440,semver,semver2</text><text class="terminal-1106739011-r2" x="732" y="117.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-4)">}</text><text class="terminal-1106739011-r2" x="744.2" y="117.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-4)">]</text><text class="terminal-1106739011-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-4)"> -</text><text class="terminal-1106739011-r2" x="244" y="142" textLength="12.2" clip-path="url(#terminal-1106739011-line-5)">[</text><text class="terminal-1106739011-r1" x="256.2" y="142" textLength="402.6" clip-path="url(#terminal-1106739011-line-5)">--export-template EXPORT_TEMPLATE</text><text class="terminal-1106739011-r2" x="658.8" y="142" textLength="12.2" clip-path="url(#terminal-1106739011-line-5)">]</text><text class="terminal-1106739011-r2" x="683.2" y="142" textLength="12.2" clip-path="url(#terminal-1106739011-line-5)">[</text><text class="terminal-1106739011-r1" x="695.4" y="142" textLength="231.8" clip-path="url(#terminal-1106739011-line-5)">--template TEMPLATE</text><text class="terminal-1106739011-r2" x="927.2" y="142" textLength="12.2" clip-path="url(#terminal-1106739011-line-5)">]</text><text class="terminal-1106739011-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1106739011-line-5)"> -</text><text class="terminal-1106739011-r2" x="244" y="166.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-6)">[</text><text class="terminal-1106739011-r1" x="256.2" y="166.4" textLength="158.6" clip-path="url(#terminal-1106739011-line-6)">--extra EXTRA</text><text class="terminal-1106739011-r2" x="414.8" y="166.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-6)">]</text><text class="terminal-1106739011-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-6)"> -</text><text class="terminal-1106739011-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-7)"> -</text><text class="terminal-1106739011-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-8)"> -</text><text class="terminal-1106739011-r1" x="0" y="239.6" textLength="231.8" clip-path="url(#terminal-1106739011-line-9)">generate changelog </text><text class="terminal-1106739011-r2" x="231.8" y="239.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-9)">(</text><text class="terminal-1106739011-r1" x="244" y="239.6" textLength="500.2" clip-path="url(#terminal-1106739011-line-9)">note that it will overwrite existing file</text><text class="terminal-1106739011-r2" x="744.2" y="239.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-9)">)</text><text class="terminal-1106739011-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-9)"> -</text><text class="terminal-1106739011-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-1106739011-line-10)"> -</text><text class="terminal-1106739011-r1" x="0" y="288.4" textLength="256.2" clip-path="url(#terminal-1106739011-line-11)">positional arguments:</text><text class="terminal-1106739011-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-11)"> -</text><text class="terminal-1106739011-r1" x="0" y="312.8" textLength="805.2" clip-path="url(#terminal-1106739011-line-12)">  rev_range             generates changelog for the given version </text><text class="terminal-1106739011-r2" x="805.2" y="312.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-12)">(</text><text class="terminal-1106739011-r1" x="817.4" y="312.8" textLength="61" clip-path="url(#terminal-1106739011-line-12)">e.g: </text><text class="terminal-1106739011-r3" x="878.4" y="312.8" textLength="36.6" clip-path="url(#terminal-1106739011-line-12)">1.5</text><text class="terminal-1106739011-r1" x="915" y="312.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-12)">.</text><text class="terminal-1106739011-r3" x="927.2" y="312.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-12)">3</text><text class="terminal-1106739011-r2" x="939.4" y="312.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-12)">)</text><text class="terminal-1106739011-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-12)"> -</text><text class="terminal-1106739011-r1" x="0" y="337.2" textLength="500.2" clip-path="url(#terminal-1106739011-line-13)">                        or version range </text><text class="terminal-1106739011-r2" x="500.2" y="337.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-13)">(</text><text class="terminal-1106739011-r1" x="512.4" y="337.2" textLength="61" clip-path="url(#terminal-1106739011-line-13)">e.g: </text><text class="terminal-1106739011-r3" x="573.4" y="337.2" textLength="36.6" clip-path="url(#terminal-1106739011-line-13)">1.5</text><text class="terminal-1106739011-r1" x="610" y="337.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-13)">.</text><text class="terminal-1106739011-r3" x="622.2" y="337.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-13)">3</text><text class="terminal-1106739011-r1" x="634.4" y="337.2" textLength="24.4" clip-path="url(#terminal-1106739011-line-13)">..</text><text class="terminal-1106739011-r3" x="658.8" y="337.2" textLength="36.6" clip-path="url(#terminal-1106739011-line-13)">1.7</text><text class="terminal-1106739011-r1" x="695.4" y="337.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-13)">.</text><text class="terminal-1106739011-r3" x="707.6" y="337.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-13)">9</text><text class="terminal-1106739011-r2" x="719.8" y="337.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-13)">)</text><text class="terminal-1106739011-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-13)"> -</text><text class="terminal-1106739011-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-14)"> -</text><text class="terminal-1106739011-r1" x="0" y="386" textLength="97.6" clip-path="url(#terminal-1106739011-line-15)">options:</text><text class="terminal-1106739011-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-1106739011-line-15)"> -</text><text class="terminal-1106739011-r1" x="0" y="410.4" textLength="671" clip-path="url(#terminal-1106739011-line-16)">  -h, --help            show this help message and exit</text><text class="terminal-1106739011-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-16)"> -</text><text class="terminal-1106739011-r1" x="0" y="434.8" textLength="585.6" clip-path="url(#terminal-1106739011-line-17)">  --dry-run             show changelog to stdout</text><text class="terminal-1106739011-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-17)"> -</text><text class="terminal-1106739011-r1" x="0" y="459.2" textLength="280.6" clip-path="url(#terminal-1106739011-line-18)">  --file-name FILE_NAME</text><text class="terminal-1106739011-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-18)"> -</text><text class="terminal-1106739011-r1" x="0" y="483.6" textLength="573.4" clip-path="url(#terminal-1106739011-line-19)">                        file name of changelog </text><text class="terminal-1106739011-r2" x="573.4" y="483.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-19)">(</text><text class="terminal-1106739011-r1" x="585.6" y="483.6" textLength="109.8" clip-path="url(#terminal-1106739011-line-19)">default: </text><text class="terminal-1106739011-r4" x="695.4" y="483.6" textLength="170.8" clip-path="url(#terminal-1106739011-line-19)">'CHANGELOG.md'</text><text class="terminal-1106739011-r2" x="866.2" y="483.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-19)">)</text><text class="terminal-1106739011-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-19)"> -</text><text class="terminal-1106739011-r1" x="0" y="508" textLength="500.2" clip-path="url(#terminal-1106739011-line-20)">  --unreleased-version UNRELEASED_VERSION</text><text class="terminal-1106739011-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-1106739011-line-20)"> -</text><text class="terminal-1106739011-r1" x="0" y="532.4" textLength="707.6" clip-path="url(#terminal-1106739011-line-21)">                        set the value for the new version </text><text class="terminal-1106739011-r2" x="707.6" y="532.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-21)">(</text><text class="terminal-1106739011-r1" x="719.8" y="532.4" textLength="207.4" clip-path="url(#terminal-1106739011-line-21)">use the tag value</text><text class="terminal-1106739011-r2" x="927.2" y="532.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-21)">)</text><text class="terminal-1106739011-r1" x="939.4" y="532.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-21)">,</text><text class="terminal-1106739011-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-21)"> -</text><text class="terminal-1106739011-r1" x="0" y="556.8" textLength="622.2" clip-path="url(#terminal-1106739011-line-22)">                        instead of using unreleased</text><text class="terminal-1106739011-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-22)"> -</text><text class="terminal-1106739011-r1" x="0" y="581.2" textLength="939.4" clip-path="url(#terminal-1106739011-line-23)">  --incremental         generates changelog from last created version, useful</text><text class="terminal-1106739011-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-23)"> -</text><text class="terminal-1106739011-r1" x="0" y="605.6" textLength="817.4" clip-path="url(#terminal-1106739011-line-24)">                        if the changelog has been manually modified</text><text class="terminal-1106739011-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-24)"> -</text><text class="terminal-1106739011-r1" x="0" y="630" textLength="280.6" clip-path="url(#terminal-1106739011-line-25)">  --start-rev START_REV</text><text class="terminal-1106739011-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-1106739011-line-25)"> -</text><text class="terminal-1106739011-r1" x="0" y="654.4" textLength="866.2" clip-path="url(#terminal-1106739011-line-26)">                        start rev of the changelog. If not set, it will</text><text class="terminal-1106739011-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-26)"> -</text><text class="terminal-1106739011-r1" x="0" y="678.8" textLength="695.4" clip-path="url(#terminal-1106739011-line-27)">                        generate changelog from the start</text><text class="terminal-1106739011-r1" x="976" y="678.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-27)"> -</text><text class="terminal-1106739011-r1" x="0" y="703.2" textLength="915" clip-path="url(#terminal-1106739011-line-28)">  --merge-prerelease    collect all changes from prereleases into next non-</text><text class="terminal-1106739011-r1" x="976" y="703.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-28)"> -</text><text class="terminal-1106739011-r1" x="0" y="727.6" textLength="951.6" clip-path="url(#terminal-1106739011-line-29)">                        prerelease. If not set, it will include prereleases in</text><text class="terminal-1106739011-r1" x="976" y="727.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-29)"> -</text><text class="terminal-1106739011-r1" x="0" y="752" textLength="451.4" clip-path="url(#terminal-1106739011-line-30)">                        the changelog</text><text class="terminal-1106739011-r1" x="976" y="752" textLength="12.2" clip-path="url(#terminal-1106739011-line-30)"> -</text><text class="terminal-1106739011-r1" x="0" y="776.4" textLength="231.8" clip-path="url(#terminal-1106739011-line-31)">  --version-scheme </text><text class="terminal-1106739011-r2" x="231.8" y="776.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-31)">{</text><text class="terminal-1106739011-r1" x="244" y="776.4" textLength="256.2" clip-path="url(#terminal-1106739011-line-31)">pep440,semver,semver2</text><text class="terminal-1106739011-r2" x="500.2" y="776.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-31)">}</text><text class="terminal-1106739011-r1" x="976" y="776.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-31)"> -</text><text class="terminal-1106739011-r1" x="0" y="800.8" textLength="549" clip-path="url(#terminal-1106739011-line-32)">                        choose version scheme</text><text class="terminal-1106739011-r1" x="976" y="800.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-32)"> -</text><text class="terminal-1106739011-r1" x="0" y="825.2" textLength="427" clip-path="url(#terminal-1106739011-line-33)">  --export-template EXPORT_TEMPLATE</text><text class="terminal-1106739011-r1" x="976" y="825.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-33)"> -</text><text class="terminal-1106739011-r1" x="0" y="849.6" textLength="927.2" clip-path="url(#terminal-1106739011-line-34)">                        Export the changelog template into this file instead</text><text class="terminal-1106739011-r1" x="976" y="849.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-34)"> -</text><text class="terminal-1106739011-r1" x="0" y="874" textLength="475.8" clip-path="url(#terminal-1106739011-line-35)">                        of rendering it</text><text class="terminal-1106739011-r1" x="976" y="874" textLength="12.2" clip-path="url(#terminal-1106739011-line-35)"> -</text><text class="terminal-1106739011-r1" x="0" y="898.4" textLength="305" clip-path="url(#terminal-1106739011-line-36)">  --template, -t TEMPLATE</text><text class="terminal-1106739011-r1" x="976" y="898.4" textLength="12.2" clip-path="url(#terminal-1106739011-line-36)"> -</text><text class="terminal-1106739011-r1" x="0" y="922.8" textLength="646.6" clip-path="url(#terminal-1106739011-line-37)">                        changelog template file name </text><text class="terminal-1106739011-r2" x="646.6" y="922.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-37)">(</text><text class="terminal-1106739011-r1" x="658.8" y="922.8" textLength="280.6" clip-path="url(#terminal-1106739011-line-37)">relative to the current</text><text class="terminal-1106739011-r1" x="976" y="922.8" textLength="12.2" clip-path="url(#terminal-1106739011-line-37)"> -</text><text class="terminal-1106739011-r1" x="0" y="947.2" textLength="500.2" clip-path="url(#terminal-1106739011-line-38)">                        working directory</text><text class="terminal-1106739011-r2" x="500.2" y="947.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-38)">)</text><text class="terminal-1106739011-r1" x="976" y="947.2" textLength="12.2" clip-path="url(#terminal-1106739011-line-38)"> -</text><text class="terminal-1106739011-r1" x="0" y="971.6" textLength="622.2" clip-path="url(#terminal-1106739011-line-39)">  --extra, -e EXTRA     a changelog extra variable </text><text class="terminal-1106739011-r2" x="622.2" y="971.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-39)">(</text><text class="terminal-1106739011-r1" x="634.4" y="971.6" textLength="146.4" clip-path="url(#terminal-1106739011-line-39)">in the form </text><text class="terminal-1106739011-r4" x="780.8" y="971.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-39)">'</text><text class="terminal-1106739011-r4" x="793" y="971.6" textLength="36.6" clip-path="url(#terminal-1106739011-line-39)">key</text><text class="terminal-1106739011-r4" x="829.6" y="971.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-39)">=</text><text class="terminal-1106739011-r4" x="841.8" y="971.6" textLength="61" clip-path="url(#terminal-1106739011-line-39)">value</text><text class="terminal-1106739011-r4" x="902.8" y="971.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-39)">'</text><text class="terminal-1106739011-r2" x="915" y="971.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-39)">)</text><text class="terminal-1106739011-r1" x="976" y="971.6" textLength="12.2" clip-path="url(#terminal-1106739011-line-39)"> -</text><text class="terminal-1106739011-r1" x="976" y="996" textLength="12.2" clip-path="url(#terminal-1106739011-line-40)"> + <g class="terminal-2926696453-matrix"> + <text class="terminal-2926696453-r1" x="0" y="20" textLength="256.2" clip-path="url(#terminal-2926696453-line-0)">$ cz changelog --help</text><text class="terminal-2926696453-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-2926696453-line-0)"> +</text><text class="terminal-2926696453-r1" x="0" y="44.4" textLength="244" clip-path="url(#terminal-2926696453-line-1)">usage: cz changelog </text><text class="terminal-2926696453-r2" x="244" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">[</text><text class="terminal-2926696453-r1" x="256.2" y="44.4" textLength="24.4" clip-path="url(#terminal-2926696453-line-1)">-h</text><text class="terminal-2926696453-r2" x="280.6" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">]</text><text class="terminal-2926696453-r2" x="305" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">[</text><text class="terminal-2926696453-r1" x="317.2" y="44.4" textLength="109.8" clip-path="url(#terminal-2926696453-line-1)">--dry-run</text><text class="terminal-2926696453-r2" x="427" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">]</text><text class="terminal-2926696453-r2" x="451.4" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">[</text><text class="terminal-2926696453-r1" x="463.6" y="44.4" textLength="256.2" clip-path="url(#terminal-2926696453-line-1)">--file-name FILE_NAME</text><text class="terminal-2926696453-r2" x="719.8" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">]</text><text class="terminal-2926696453-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)"> +</text><text class="terminal-2926696453-r2" x="244" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)">[</text><text class="terminal-2926696453-r1" x="256.2" y="68.8" textLength="475.8" clip-path="url(#terminal-2926696453-line-2)">--unreleased-version UNRELEASED_VERSION</text><text class="terminal-2926696453-r2" x="732" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)">]</text><text class="terminal-2926696453-r2" x="756.4" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)">[</text><text class="terminal-2926696453-r1" x="768.6" y="68.8" textLength="158.6" clip-path="url(#terminal-2926696453-line-2)">--incremental</text><text class="terminal-2926696453-r2" x="927.2" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)">]</text><text class="terminal-2926696453-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)"> +</text><text class="terminal-2926696453-r2" x="244" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)">[</text><text class="terminal-2926696453-r1" x="256.2" y="93.2" textLength="256.2" clip-path="url(#terminal-2926696453-line-3)">--start-rev START_REV</text><text class="terminal-2926696453-r2" x="512.4" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)">]</text><text class="terminal-2926696453-r2" x="536.8" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)">[</text><text class="terminal-2926696453-r1" x="549" y="93.2" textLength="219.6" clip-path="url(#terminal-2926696453-line-3)">--merge-prerelease</text><text class="terminal-2926696453-r2" x="768.6" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)">]</text><text class="terminal-2926696453-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)"> +</text><text class="terminal-2926696453-r2" x="244" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)">[</text><text class="terminal-2926696453-r1" x="256.2" y="117.6" textLength="207.4" clip-path="url(#terminal-2926696453-line-4)">--version-scheme </text><text class="terminal-2926696453-r2" x="463.6" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)">{</text><text class="terminal-2926696453-r1" x="475.8" y="117.6" textLength="256.2" clip-path="url(#terminal-2926696453-line-4)">pep440,semver,semver2</text><text class="terminal-2926696453-r2" x="732" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)">}</text><text class="terminal-2926696453-r2" x="744.2" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)">]</text><text class="terminal-2926696453-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)"> +</text><text class="terminal-2926696453-r2" x="244" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)">[</text><text class="terminal-2926696453-r1" x="256.2" y="142" textLength="402.6" clip-path="url(#terminal-2926696453-line-5)">--export-template EXPORT_TEMPLATE</text><text class="terminal-2926696453-r2" x="658.8" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)">]</text><text class="terminal-2926696453-r2" x="683.2" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)">[</text><text class="terminal-2926696453-r1" x="695.4" y="142" textLength="231.8" clip-path="url(#terminal-2926696453-line-5)">--template TEMPLATE</text><text class="terminal-2926696453-r2" x="927.2" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)">]</text><text class="terminal-2926696453-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)"> +</text><text class="terminal-2926696453-r2" x="244" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)">[</text><text class="terminal-2926696453-r1" x="256.2" y="166.4" textLength="158.6" clip-path="url(#terminal-2926696453-line-6)">--extra EXTRA</text><text class="terminal-2926696453-r2" x="414.8" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)">]</text><text class="terminal-2926696453-r2" x="439.2" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)">[</text><text class="terminal-2926696453-r1" x="451.4" y="166.4" textLength="280.6" clip-path="url(#terminal-2926696453-line-6)">--tag-format TAG_FORMAT</text><text class="terminal-2926696453-r2" x="732" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)">]</text><text class="terminal-2926696453-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)"> +</text><text class="terminal-2926696453-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-7)"> +</text><text class="terminal-2926696453-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-8)"> +</text><text class="terminal-2926696453-r1" x="0" y="239.6" textLength="231.8" clip-path="url(#terminal-2926696453-line-9)">generate changelog </text><text class="terminal-2926696453-r2" x="231.8" y="239.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-9)">(</text><text class="terminal-2926696453-r1" x="244" y="239.6" textLength="500.2" clip-path="url(#terminal-2926696453-line-9)">note that it will overwrite existing file</text><text class="terminal-2926696453-r2" x="744.2" y="239.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-9)">)</text><text class="terminal-2926696453-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-9)"> +</text><text class="terminal-2926696453-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-2926696453-line-10)"> +</text><text class="terminal-2926696453-r1" x="0" y="288.4" textLength="256.2" clip-path="url(#terminal-2926696453-line-11)">positional arguments:</text><text class="terminal-2926696453-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-11)"> +</text><text class="terminal-2926696453-r1" x="0" y="312.8" textLength="805.2" clip-path="url(#terminal-2926696453-line-12)">  rev_range             generates changelog for the given version </text><text class="terminal-2926696453-r2" x="805.2" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)">(</text><text class="terminal-2926696453-r1" x="817.4" y="312.8" textLength="61" clip-path="url(#terminal-2926696453-line-12)">e.g: </text><text class="terminal-2926696453-r3" x="878.4" y="312.8" textLength="36.6" clip-path="url(#terminal-2926696453-line-12)">1.5</text><text class="terminal-2926696453-r1" x="915" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)">.</text><text class="terminal-2926696453-r3" x="927.2" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)">3</text><text class="terminal-2926696453-r2" x="939.4" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)">)</text><text class="terminal-2926696453-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)"> +</text><text class="terminal-2926696453-r1" x="0" y="337.2" textLength="500.2" clip-path="url(#terminal-2926696453-line-13)">                        or version range </text><text class="terminal-2926696453-r2" x="500.2" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">(</text><text class="terminal-2926696453-r1" x="512.4" y="337.2" textLength="61" clip-path="url(#terminal-2926696453-line-13)">e.g: </text><text class="terminal-2926696453-r3" x="573.4" y="337.2" textLength="36.6" clip-path="url(#terminal-2926696453-line-13)">1.5</text><text class="terminal-2926696453-r1" x="610" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">.</text><text class="terminal-2926696453-r3" x="622.2" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">3</text><text class="terminal-2926696453-r1" x="634.4" y="337.2" textLength="24.4" clip-path="url(#terminal-2926696453-line-13)">..</text><text class="terminal-2926696453-r3" x="658.8" y="337.2" textLength="36.6" clip-path="url(#terminal-2926696453-line-13)">1.7</text><text class="terminal-2926696453-r1" x="695.4" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">.</text><text class="terminal-2926696453-r3" x="707.6" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">9</text><text class="terminal-2926696453-r2" x="719.8" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">)</text><text class="terminal-2926696453-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)"> +</text><text class="terminal-2926696453-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-14)"> +</text><text class="terminal-2926696453-r1" x="0" y="386" textLength="97.6" clip-path="url(#terminal-2926696453-line-15)">options:</text><text class="terminal-2926696453-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-2926696453-line-15)"> +</text><text class="terminal-2926696453-r1" x="0" y="410.4" textLength="671" clip-path="url(#terminal-2926696453-line-16)">  -h, --help            show this help message and exit</text><text class="terminal-2926696453-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-16)"> +</text><text class="terminal-2926696453-r1" x="0" y="434.8" textLength="585.6" clip-path="url(#terminal-2926696453-line-17)">  --dry-run             show changelog to stdout</text><text class="terminal-2926696453-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-17)"> +</text><text class="terminal-2926696453-r1" x="0" y="459.2" textLength="280.6" clip-path="url(#terminal-2926696453-line-18)">  --file-name FILE_NAME</text><text class="terminal-2926696453-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-18)"> +</text><text class="terminal-2926696453-r1" x="0" y="483.6" textLength="573.4" clip-path="url(#terminal-2926696453-line-19)">                        file name of changelog </text><text class="terminal-2926696453-r2" x="573.4" y="483.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-19)">(</text><text class="terminal-2926696453-r1" x="585.6" y="483.6" textLength="109.8" clip-path="url(#terminal-2926696453-line-19)">default: </text><text class="terminal-2926696453-r4" x="695.4" y="483.6" textLength="170.8" clip-path="url(#terminal-2926696453-line-19)">'CHANGELOG.md'</text><text class="terminal-2926696453-r2" x="866.2" y="483.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-19)">)</text><text class="terminal-2926696453-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-19)"> +</text><text class="terminal-2926696453-r1" x="0" y="508" textLength="500.2" clip-path="url(#terminal-2926696453-line-20)">  --unreleased-version UNRELEASED_VERSION</text><text class="terminal-2926696453-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-2926696453-line-20)"> +</text><text class="terminal-2926696453-r1" x="0" y="532.4" textLength="707.6" clip-path="url(#terminal-2926696453-line-21)">                        set the value for the new version </text><text class="terminal-2926696453-r2" x="707.6" y="532.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-21)">(</text><text class="terminal-2926696453-r1" x="719.8" y="532.4" textLength="207.4" clip-path="url(#terminal-2926696453-line-21)">use the tag value</text><text class="terminal-2926696453-r2" x="927.2" y="532.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-21)">)</text><text class="terminal-2926696453-r1" x="939.4" y="532.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-21)">,</text><text class="terminal-2926696453-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-21)"> +</text><text class="terminal-2926696453-r1" x="0" y="556.8" textLength="622.2" clip-path="url(#terminal-2926696453-line-22)">                        instead of using unreleased</text><text class="terminal-2926696453-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-22)"> +</text><text class="terminal-2926696453-r1" x="0" y="581.2" textLength="939.4" clip-path="url(#terminal-2926696453-line-23)">  --incremental         generates changelog from last created version, useful</text><text class="terminal-2926696453-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-23)"> +</text><text class="terminal-2926696453-r1" x="0" y="605.6" textLength="817.4" clip-path="url(#terminal-2926696453-line-24)">                        if the changelog has been manually modified</text><text class="terminal-2926696453-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-24)"> +</text><text class="terminal-2926696453-r1" x="0" y="630" textLength="280.6" clip-path="url(#terminal-2926696453-line-25)">  --start-rev START_REV</text><text class="terminal-2926696453-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-2926696453-line-25)"> +</text><text class="terminal-2926696453-r1" x="0" y="654.4" textLength="866.2" clip-path="url(#terminal-2926696453-line-26)">                        start rev of the changelog. If not set, it will</text><text class="terminal-2926696453-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-26)"> +</text><text class="terminal-2926696453-r1" x="0" y="678.8" textLength="695.4" clip-path="url(#terminal-2926696453-line-27)">                        generate changelog from the start</text><text class="terminal-2926696453-r1" x="976" y="678.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-27)"> +</text><text class="terminal-2926696453-r1" x="0" y="703.2" textLength="915" clip-path="url(#terminal-2926696453-line-28)">  --merge-prerelease    collect all changes from prereleases into next non-</text><text class="terminal-2926696453-r1" x="976" y="703.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-28)"> +</text><text class="terminal-2926696453-r1" x="0" y="727.6" textLength="951.6" clip-path="url(#terminal-2926696453-line-29)">                        prerelease. If not set, it will include prereleases in</text><text class="terminal-2926696453-r1" x="976" y="727.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-29)"> +</text><text class="terminal-2926696453-r1" x="0" y="752" textLength="451.4" clip-path="url(#terminal-2926696453-line-30)">                        the changelog</text><text class="terminal-2926696453-r1" x="976" y="752" textLength="12.2" clip-path="url(#terminal-2926696453-line-30)"> +</text><text class="terminal-2926696453-r1" x="0" y="776.4" textLength="231.8" clip-path="url(#terminal-2926696453-line-31)">  --version-scheme </text><text class="terminal-2926696453-r2" x="231.8" y="776.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-31)">{</text><text class="terminal-2926696453-r1" x="244" y="776.4" textLength="256.2" clip-path="url(#terminal-2926696453-line-31)">pep440,semver,semver2</text><text class="terminal-2926696453-r2" x="500.2" y="776.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-31)">}</text><text class="terminal-2926696453-r1" x="976" y="776.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-31)"> +</text><text class="terminal-2926696453-r1" x="0" y="800.8" textLength="549" clip-path="url(#terminal-2926696453-line-32)">                        choose version scheme</text><text class="terminal-2926696453-r1" x="976" y="800.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-32)"> +</text><text class="terminal-2926696453-r1" x="0" y="825.2" textLength="427" clip-path="url(#terminal-2926696453-line-33)">  --export-template EXPORT_TEMPLATE</text><text class="terminal-2926696453-r1" x="976" y="825.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-33)"> +</text><text class="terminal-2926696453-r1" x="0" y="849.6" textLength="927.2" clip-path="url(#terminal-2926696453-line-34)">                        Export the changelog template into this file instead</text><text class="terminal-2926696453-r1" x="976" y="849.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-34)"> +</text><text class="terminal-2926696453-r1" x="0" y="874" textLength="475.8" clip-path="url(#terminal-2926696453-line-35)">                        of rendering it</text><text class="terminal-2926696453-r1" x="976" y="874" textLength="12.2" clip-path="url(#terminal-2926696453-line-35)"> +</text><text class="terminal-2926696453-r1" x="0" y="898.4" textLength="305" clip-path="url(#terminal-2926696453-line-36)">  --template, -t TEMPLATE</text><text class="terminal-2926696453-r1" x="976" y="898.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-36)"> +</text><text class="terminal-2926696453-r1" x="0" y="922.8" textLength="646.6" clip-path="url(#terminal-2926696453-line-37)">                        changelog template file name </text><text class="terminal-2926696453-r2" x="646.6" y="922.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-37)">(</text><text class="terminal-2926696453-r1" x="658.8" y="922.8" textLength="280.6" clip-path="url(#terminal-2926696453-line-37)">relative to the current</text><text class="terminal-2926696453-r1" x="976" y="922.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-37)"> +</text><text class="terminal-2926696453-r1" x="0" y="947.2" textLength="500.2" clip-path="url(#terminal-2926696453-line-38)">                        working directory</text><text class="terminal-2926696453-r2" x="500.2" y="947.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-38)">)</text><text class="terminal-2926696453-r1" x="976" y="947.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-38)"> +</text><text class="terminal-2926696453-r1" x="0" y="971.6" textLength="622.2" clip-path="url(#terminal-2926696453-line-39)">  --extra, -e EXTRA     a changelog extra variable </text><text class="terminal-2926696453-r2" x="622.2" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">(</text><text class="terminal-2926696453-r1" x="634.4" y="971.6" textLength="146.4" clip-path="url(#terminal-2926696453-line-39)">in the form </text><text class="terminal-2926696453-r4" x="780.8" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">'</text><text class="terminal-2926696453-r4" x="793" y="971.6" textLength="36.6" clip-path="url(#terminal-2926696453-line-39)">key</text><text class="terminal-2926696453-r4" x="829.6" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">=</text><text class="terminal-2926696453-r4" x="841.8" y="971.6" textLength="61" clip-path="url(#terminal-2926696453-line-39)">value</text><text class="terminal-2926696453-r4" x="902.8" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">'</text><text class="terminal-2926696453-r2" x="915" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">)</text><text class="terminal-2926696453-r1" x="976" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)"> +</text><text class="terminal-2926696453-r1" x="0" y="996" textLength="305" clip-path="url(#terminal-2926696453-line-40)">  --tag-format TAG_FORMAT</text><text class="terminal-2926696453-r1" x="976" y="996" textLength="12.2" clip-path="url(#terminal-2926696453-line-40)"> +</text><text class="terminal-2926696453-r1" x="0" y="1020.4" textLength="878.4" clip-path="url(#terminal-2926696453-line-41)">                        The format of the tag, wrap around simple quotes</text><text class="terminal-2926696453-r1" x="976" y="1020.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-41)"> +</text><text class="terminal-2926696453-r1" x="976" y="1044.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-42)"> </text> </g> </g> diff --git a/docs/images/cli_help/cz_check___help.svg b/docs/images/cli_help/cz_check___help.svg index 690bfec684..397c0a2eeb 100644 --- a/docs/images/cli_help/cz_check___help.svg +++ b/docs/images/cli_help/cz_check___help.svg @@ -1,4 +1,4 @@ -<svg class="rich-terminal" viewBox="0 0 994 708.8" xmlns="http://www.w3.org/2000/svg"> +<svg class="rich-terminal" viewBox="0 0 994 782.0" xmlns="http://www.w3.org/2000/svg"> <!-- Generated with Rich https://www.textualize.io --> <style> @@ -19,146 +19,158 @@ font-weight: 700; } - .terminal-1360575461-matrix { + .terminal-2151679122-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1360575461-title { + .terminal-2151679122-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1360575461-r1 { fill: #c5c8c6 } -.terminal-1360575461-r2 { fill: #c5c8c6;font-weight: bold } -.terminal-1360575461-r3 { fill: #d0b344 } -.terminal-1360575461-r4 { fill: #68a0b3;font-weight: bold } + .terminal-2151679122-r1 { fill: #c5c8c6 } +.terminal-2151679122-r2 { fill: #c5c8c6;font-weight: bold } +.terminal-2151679122-r3 { fill: #d0b344 } +.terminal-2151679122-r4 { fill: #68a0b3;font-weight: bold } </style> <defs> - <clipPath id="terminal-1360575461-clip-terminal"> - <rect x="0" y="0" width="975.0" height="657.8" /> + <clipPath id="terminal-2151679122-clip-terminal"> + <rect x="0" y="0" width="975.0" height="731.0" /> </clipPath> - <clipPath id="terminal-1360575461-line-0"> + <clipPath id="terminal-2151679122-line-0"> <rect x="0" y="1.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-1"> +<clipPath id="terminal-2151679122-line-1"> <rect x="0" y="25.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-2"> +<clipPath id="terminal-2151679122-line-2"> <rect x="0" y="50.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-3"> +<clipPath id="terminal-2151679122-line-3"> <rect x="0" y="74.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-4"> +<clipPath id="terminal-2151679122-line-4"> <rect x="0" y="99.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-5"> +<clipPath id="terminal-2151679122-line-5"> <rect x="0" y="123.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-6"> +<clipPath id="terminal-2151679122-line-6"> <rect x="0" y="147.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-7"> +<clipPath id="terminal-2151679122-line-7"> <rect x="0" y="172.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-8"> +<clipPath id="terminal-2151679122-line-8"> <rect x="0" y="196.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-9"> +<clipPath id="terminal-2151679122-line-9"> <rect x="0" y="221.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-10"> +<clipPath id="terminal-2151679122-line-10"> <rect x="0" y="245.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-11"> +<clipPath id="terminal-2151679122-line-11"> <rect x="0" y="269.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-12"> +<clipPath id="terminal-2151679122-line-12"> <rect x="0" y="294.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-13"> +<clipPath id="terminal-2151679122-line-13"> <rect x="0" y="318.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-14"> +<clipPath id="terminal-2151679122-line-14"> <rect x="0" y="343.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-15"> +<clipPath id="terminal-2151679122-line-15"> <rect x="0" y="367.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-16"> +<clipPath id="terminal-2151679122-line-16"> <rect x="0" y="391.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-17"> +<clipPath id="terminal-2151679122-line-17"> <rect x="0" y="416.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-18"> +<clipPath id="terminal-2151679122-line-18"> <rect x="0" y="440.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-19"> +<clipPath id="terminal-2151679122-line-19"> <rect x="0" y="465.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-20"> +<clipPath id="terminal-2151679122-line-20"> <rect x="0" y="489.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-21"> +<clipPath id="terminal-2151679122-line-21"> <rect x="0" y="513.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-22"> +<clipPath id="terminal-2151679122-line-22"> <rect x="0" y="538.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-23"> +<clipPath id="terminal-2151679122-line-23"> <rect x="0" y="562.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-24"> +<clipPath id="terminal-2151679122-line-24"> <rect x="0" y="587.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-1360575461-line-25"> +<clipPath id="terminal-2151679122-line-25"> <rect x="0" y="611.5" width="976" height="24.65"/> </clipPath> +<clipPath id="terminal-2151679122-line-26"> + <rect x="0" y="635.9" width="976" height="24.65"/> + </clipPath> +<clipPath id="terminal-2151679122-line-27"> + <rect x="0" y="660.3" width="976" height="24.65"/> + </clipPath> +<clipPath id="terminal-2151679122-line-28"> + <rect x="0" y="684.7" width="976" height="24.65"/> + </clipPath> </defs> - <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="706.8" rx="8"/> + <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="780" rx="8"/> <g transform="translate(26,22)"> <circle cx="0" cy="0" r="7" fill="#ff5f57"/> <circle cx="22" cy="0" r="7" fill="#febc2e"/> <circle cx="44" cy="0" r="7" fill="#28c840"/> </g> - <g transform="translate(9, 41)" clip-path="url(#terminal-1360575461-clip-terminal)"> + <g transform="translate(9, 41)" clip-path="url(#terminal-2151679122-clip-terminal)"> - <g class="terminal-1360575461-matrix"> - <text class="terminal-1360575461-r1" x="0" y="20" textLength="207.4" clip-path="url(#terminal-1360575461-line-0)">$ cz check --help</text><text class="terminal-1360575461-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1360575461-line-0)"> -</text><text class="terminal-1360575461-r1" x="0" y="44.4" textLength="195.2" clip-path="url(#terminal-1360575461-line-1)">usage: cz check </text><text class="terminal-1360575461-r2" x="195.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-1)">[</text><text class="terminal-1360575461-r1" x="207.4" y="44.4" textLength="24.4" clip-path="url(#terminal-1360575461-line-1)">-h</text><text class="terminal-1360575461-r2" x="231.8" y="44.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-1)">]</text><text class="terminal-1360575461-r2" x="256.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-1)">[</text><text class="terminal-1360575461-r1" x="268.4" y="44.4" textLength="427" clip-path="url(#terminal-1360575461-line-1)">--commit-msg-file COMMIT_MSG_FILE |</text><text class="terminal-1360575461-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-1)"> -</text><text class="terminal-1360575461-r1" x="0" y="68.8" textLength="610" clip-path="url(#terminal-1360575461-line-2)">                --rev-range REV_RANGE | -m MESSAGE</text><text class="terminal-1360575461-r2" x="610" y="68.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-2)">]</text><text class="terminal-1360575461-r2" x="634.4" y="68.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-2)">[</text><text class="terminal-1360575461-r1" x="646.6" y="68.8" textLength="158.6" clip-path="url(#terminal-1360575461-line-2)">--allow-abort</text><text class="terminal-1360575461-r2" x="805.2" y="68.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-2)">]</text><text class="terminal-1360575461-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-2)"> -</text><text class="terminal-1360575461-r2" x="195.2" y="93.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-3)">[</text><text class="terminal-1360575461-r1" x="207.4" y="93.2" textLength="231.8" clip-path="url(#terminal-1360575461-line-3)">--allowed-prefixes </text><text class="terminal-1360575461-r2" x="439.2" y="93.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-3)">[</text><text class="terminal-1360575461-r1" x="451.4" y="93.2" textLength="207.4" clip-path="url(#terminal-1360575461-line-3)">ALLOWED_PREFIXES </text><text class="terminal-1360575461-r3" x="658.8" y="93.2" textLength="36.6" clip-path="url(#terminal-1360575461-line-3)">...</text><text class="terminal-1360575461-r2" x="695.4" y="93.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-3)">]</text><text class="terminal-1360575461-r2" x="707.6" y="93.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-3)">]</text><text class="terminal-1360575461-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-3)"> -</text><text class="terminal-1360575461-r2" x="195.2" y="117.6" textLength="12.2" clip-path="url(#terminal-1360575461-line-4)">[</text><text class="terminal-1360575461-r1" x="207.4" y="117.6" textLength="280.6" clip-path="url(#terminal-1360575461-line-4)">-l MESSAGE_LENGTH_LIMIT</text><text class="terminal-1360575461-r2" x="488" y="117.6" textLength="12.2" clip-path="url(#terminal-1360575461-line-4)">]</text><text class="terminal-1360575461-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1360575461-line-4)"> -</text><text class="terminal-1360575461-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1360575461-line-5)"> -</text><text class="terminal-1360575461-r1" x="0" y="166.4" textLength="744.2" clip-path="url(#terminal-1360575461-line-6)">validates that a commit message matches the commitizen schema</text><text class="terminal-1360575461-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-6)"> -</text><text class="terminal-1360575461-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-7)"> -</text><text class="terminal-1360575461-r1" x="0" y="215.2" textLength="97.6" clip-path="url(#terminal-1360575461-line-8)">options:</text><text class="terminal-1360575461-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-8)"> -</text><text class="terminal-1360575461-r1" x="0" y="239.6" textLength="671" clip-path="url(#terminal-1360575461-line-9)">  -h, --help            show this help message and exit</text><text class="terminal-1360575461-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-1360575461-line-9)"> -</text><text class="terminal-1360575461-r1" x="0" y="264" textLength="427" clip-path="url(#terminal-1360575461-line-10)">  --commit-msg-file COMMIT_MSG_FILE</text><text class="terminal-1360575461-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-1360575461-line-10)"> -</text><text class="terminal-1360575461-r1" x="0" y="288.4" textLength="915" clip-path="url(#terminal-1360575461-line-11)">                        ask for the name of the temporal file that contains</text><text class="terminal-1360575461-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-11)"> -</text><text class="terminal-1360575461-r1" x="0" y="312.8" textLength="902.8" clip-path="url(#terminal-1360575461-line-12)">                        the commit message. Using it in a git hook script:</text><text class="terminal-1360575461-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-12)"> -</text><text class="terminal-1360575461-r3" x="292.8" y="337.2" textLength="97.6" clip-path="url(#terminal-1360575461-line-13)">MSG_FILE</text><text class="terminal-1360575461-r1" x="390.4" y="337.2" textLength="24.4" clip-path="url(#terminal-1360575461-line-13)">=$</text><text class="terminal-1360575461-r4" x="414.8" y="337.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-13)">1</text><text class="terminal-1360575461-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-13)"> -</text><text class="terminal-1360575461-r1" x="0" y="361.6" textLength="280.6" clip-path="url(#terminal-1360575461-line-14)">  --rev-range REV_RANGE</text><text class="terminal-1360575461-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-1360575461-line-14)"> -</text><text class="terminal-1360575461-r1" x="0" y="386" textLength="854" clip-path="url(#terminal-1360575461-line-15)">                        a range of git rev to check. e.g, master..HEAD</text><text class="terminal-1360575461-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-1360575461-line-15)"> -</text><text class="terminal-1360575461-r1" x="0" y="410.4" textLength="280.6" clip-path="url(#terminal-1360575461-line-16)">  -m, --message MESSAGE</text><text class="terminal-1360575461-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-16)"> -</text><text class="terminal-1360575461-r1" x="0" y="434.8" textLength="768.6" clip-path="url(#terminal-1360575461-line-17)">                        commit message that needs to be checked</text><text class="terminal-1360575461-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-17)"> -</text><text class="terminal-1360575461-r1" x="0" y="459.2" textLength="927.2" clip-path="url(#terminal-1360575461-line-18)">  --allow-abort         allow empty commit messages, which typically abort a</text><text class="terminal-1360575461-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-18)"> -</text><text class="terminal-1360575461-r1" x="0" y="483.6" textLength="366" clip-path="url(#terminal-1360575461-line-19)">                        commit</text><text class="terminal-1360575461-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-1360575461-line-19)"> -</text><text class="terminal-1360575461-r1" x="0" y="508" textLength="256.2" clip-path="url(#terminal-1360575461-line-20)">  --allowed-prefixes </text><text class="terminal-1360575461-r2" x="256.2" y="508" textLength="12.2" clip-path="url(#terminal-1360575461-line-20)">[</text><text class="terminal-1360575461-r1" x="268.4" y="508" textLength="207.4" clip-path="url(#terminal-1360575461-line-20)">ALLOWED_PREFIXES </text><text class="terminal-1360575461-r3" x="475.8" y="508" textLength="36.6" clip-path="url(#terminal-1360575461-line-20)">...</text><text class="terminal-1360575461-r2" x="512.4" y="508" textLength="12.2" clip-path="url(#terminal-1360575461-line-20)">]</text><text class="terminal-1360575461-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-1360575461-line-20)"> -</text><text class="terminal-1360575461-r1" x="0" y="532.4" textLength="951.6" clip-path="url(#terminal-1360575461-line-21)">                        allowed commit message prefixes. If the message starts</text><text class="terminal-1360575461-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-21)"> -</text><text class="terminal-1360575461-r1" x="0" y="556.8" textLength="951.6" clip-path="url(#terminal-1360575461-line-22)">                        by one of these prefixes, the message won't be checked</text><text class="terminal-1360575461-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-22)"> -</text><text class="terminal-1360575461-r1" x="0" y="581.2" textLength="500.2" clip-path="url(#terminal-1360575461-line-23)">                        against the regex</text><text class="terminal-1360575461-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-23)"> -</text><text class="terminal-1360575461-r1" x="0" y="605.6" textLength="597.8" clip-path="url(#terminal-1360575461-line-24)">  -l, --message-length-limit MESSAGE_LENGTH_LIMIT</text><text class="terminal-1360575461-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-1360575461-line-24)"> -</text><text class="terminal-1360575461-r1" x="0" y="630" textLength="732" clip-path="url(#terminal-1360575461-line-25)">                        length limit of the commit message; </text><text class="terminal-1360575461-r4" x="732" y="630" textLength="12.2" clip-path="url(#terminal-1360575461-line-25)">0</text><text class="terminal-1360575461-r1" x="744.2" y="630" textLength="158.6" clip-path="url(#terminal-1360575461-line-25)"> for no limit</text><text class="terminal-1360575461-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-1360575461-line-25)"> -</text><text class="terminal-1360575461-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-26)"> + <g class="terminal-2151679122-matrix"> + <text class="terminal-2151679122-r1" x="0" y="20" textLength="207.4" clip-path="url(#terminal-2151679122-line-0)">$ cz check --help</text><text class="terminal-2151679122-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-2151679122-line-0)"> +</text><text class="terminal-2151679122-r1" x="0" y="44.4" textLength="195.2" clip-path="url(#terminal-2151679122-line-1)">usage: cz check </text><text class="terminal-2151679122-r2" x="195.2" y="44.4" textLength="12.2" clip-path="url(#terminal-2151679122-line-1)">[</text><text class="terminal-2151679122-r1" x="207.4" y="44.4" textLength="24.4" clip-path="url(#terminal-2151679122-line-1)">-h</text><text class="terminal-2151679122-r2" x="231.8" y="44.4" textLength="12.2" clip-path="url(#terminal-2151679122-line-1)">]</text><text class="terminal-2151679122-r2" x="256.2" y="44.4" textLength="12.2" clip-path="url(#terminal-2151679122-line-1)">[</text><text class="terminal-2151679122-r1" x="268.4" y="44.4" textLength="427" clip-path="url(#terminal-2151679122-line-1)">--commit-msg-file COMMIT_MSG_FILE |</text><text class="terminal-2151679122-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-2151679122-line-1)"> +</text><text class="terminal-2151679122-r1" x="0" y="68.8" textLength="671" clip-path="url(#terminal-2151679122-line-2)">                --rev-range REV_RANGE | -d | -m MESSAGE</text><text class="terminal-2151679122-r2" x="671" y="68.8" textLength="12.2" clip-path="url(#terminal-2151679122-line-2)">]</text><text class="terminal-2151679122-r2" x="695.4" y="68.8" textLength="12.2" clip-path="url(#terminal-2151679122-line-2)">[</text><text class="terminal-2151679122-r1" x="707.6" y="68.8" textLength="158.6" clip-path="url(#terminal-2151679122-line-2)">--allow-abort</text><text class="terminal-2151679122-r2" x="866.2" y="68.8" textLength="12.2" clip-path="url(#terminal-2151679122-line-2)">]</text><text class="terminal-2151679122-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-2151679122-line-2)"> +</text><text class="terminal-2151679122-r2" x="195.2" y="93.2" textLength="12.2" clip-path="url(#terminal-2151679122-line-3)">[</text><text class="terminal-2151679122-r1" x="207.4" y="93.2" textLength="231.8" clip-path="url(#terminal-2151679122-line-3)">--allowed-prefixes </text><text class="terminal-2151679122-r2" x="439.2" y="93.2" textLength="12.2" clip-path="url(#terminal-2151679122-line-3)">[</text><text class="terminal-2151679122-r1" x="451.4" y="93.2" textLength="207.4" clip-path="url(#terminal-2151679122-line-3)">ALLOWED_PREFIXES </text><text class="terminal-2151679122-r3" x="658.8" y="93.2" textLength="36.6" clip-path="url(#terminal-2151679122-line-3)">...</text><text class="terminal-2151679122-r2" x="695.4" y="93.2" textLength="12.2" clip-path="url(#terminal-2151679122-line-3)">]</text><text class="terminal-2151679122-r2" x="707.6" y="93.2" textLength="12.2" clip-path="url(#terminal-2151679122-line-3)">]</text><text class="terminal-2151679122-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-2151679122-line-3)"> +</text><text class="terminal-2151679122-r2" x="195.2" y="117.6" textLength="12.2" clip-path="url(#terminal-2151679122-line-4)">[</text><text class="terminal-2151679122-r1" x="207.4" y="117.6" textLength="280.6" clip-path="url(#terminal-2151679122-line-4)">-l MESSAGE_LENGTH_LIMIT</text><text class="terminal-2151679122-r2" x="488" y="117.6" textLength="12.2" clip-path="url(#terminal-2151679122-line-4)">]</text><text class="terminal-2151679122-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-2151679122-line-4)"> +</text><text class="terminal-2151679122-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-2151679122-line-5)"> +</text><text class="terminal-2151679122-r1" x="0" y="166.4" textLength="744.2" clip-path="url(#terminal-2151679122-line-6)">validates that a commit message matches the commitizen schema</text><text class="terminal-2151679122-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-2151679122-line-6)"> +</text><text class="terminal-2151679122-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-2151679122-line-7)"> +</text><text class="terminal-2151679122-r1" x="0" y="215.2" textLength="97.6" clip-path="url(#terminal-2151679122-line-8)">options:</text><text class="terminal-2151679122-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-2151679122-line-8)"> +</text><text class="terminal-2151679122-r1" x="0" y="239.6" textLength="671" clip-path="url(#terminal-2151679122-line-9)">  -h, --help            show this help message and exit</text><text class="terminal-2151679122-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-2151679122-line-9)"> +</text><text class="terminal-2151679122-r1" x="0" y="264" textLength="427" clip-path="url(#terminal-2151679122-line-10)">  --commit-msg-file COMMIT_MSG_FILE</text><text class="terminal-2151679122-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-2151679122-line-10)"> +</text><text class="terminal-2151679122-r1" x="0" y="288.4" textLength="915" clip-path="url(#terminal-2151679122-line-11)">                        ask for the name of the temporal file that contains</text><text class="terminal-2151679122-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-2151679122-line-11)"> +</text><text class="terminal-2151679122-r1" x="0" y="312.8" textLength="902.8" clip-path="url(#terminal-2151679122-line-12)">                        the commit message. Using it in a git hook script:</text><text class="terminal-2151679122-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-2151679122-line-12)"> +</text><text class="terminal-2151679122-r3" x="292.8" y="337.2" textLength="97.6" clip-path="url(#terminal-2151679122-line-13)">MSG_FILE</text><text class="terminal-2151679122-r1" x="390.4" y="337.2" textLength="24.4" clip-path="url(#terminal-2151679122-line-13)">=$</text><text class="terminal-2151679122-r4" x="414.8" y="337.2" textLength="12.2" clip-path="url(#terminal-2151679122-line-13)">1</text><text class="terminal-2151679122-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-2151679122-line-13)"> +</text><text class="terminal-2151679122-r1" x="0" y="361.6" textLength="280.6" clip-path="url(#terminal-2151679122-line-14)">  --rev-range REV_RANGE</text><text class="terminal-2151679122-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-2151679122-line-14)"> +</text><text class="terminal-2151679122-r1" x="0" y="386" textLength="854" clip-path="url(#terminal-2151679122-line-15)">                        a range of git rev to check. e.g, master..HEAD</text><text class="terminal-2151679122-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-2151679122-line-15)"> +</text><text class="terminal-2151679122-r1" x="0" y="410.4" textLength="305" clip-path="url(#terminal-2151679122-line-16)">  -d, --use-default-range</text><text class="terminal-2151679122-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-2151679122-line-16)"> +</text><text class="terminal-2151679122-r1" x="0" y="434.8" textLength="817.4" clip-path="url(#terminal-2151679122-line-17)">                        check from the default branch to HEAD. e.g,</text><text class="terminal-2151679122-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-2151679122-line-17)"> +</text><text class="terminal-2151679122-r1" x="0" y="459.2" textLength="683.2" clip-path="url(#terminal-2151679122-line-18)">                        refs/remotes/origin/master..HEAD</text><text class="terminal-2151679122-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-2151679122-line-18)"> +</text><text class="terminal-2151679122-r1" x="0" y="483.6" textLength="280.6" clip-path="url(#terminal-2151679122-line-19)">  -m, --message MESSAGE</text><text class="terminal-2151679122-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-2151679122-line-19)"> +</text><text class="terminal-2151679122-r1" x="0" y="508" textLength="768.6" clip-path="url(#terminal-2151679122-line-20)">                        commit message that needs to be checked</text><text class="terminal-2151679122-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-2151679122-line-20)"> +</text><text class="terminal-2151679122-r1" x="0" y="532.4" textLength="927.2" clip-path="url(#terminal-2151679122-line-21)">  --allow-abort         allow empty commit messages, which typically abort a</text><text class="terminal-2151679122-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-2151679122-line-21)"> +</text><text class="terminal-2151679122-r1" x="0" y="556.8" textLength="366" clip-path="url(#terminal-2151679122-line-22)">                        commit</text><text class="terminal-2151679122-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-2151679122-line-22)"> +</text><text class="terminal-2151679122-r1" x="0" y="581.2" textLength="256.2" clip-path="url(#terminal-2151679122-line-23)">  --allowed-prefixes </text><text class="terminal-2151679122-r2" x="256.2" y="581.2" textLength="12.2" clip-path="url(#terminal-2151679122-line-23)">[</text><text class="terminal-2151679122-r1" x="268.4" y="581.2" textLength="207.4" clip-path="url(#terminal-2151679122-line-23)">ALLOWED_PREFIXES </text><text class="terminal-2151679122-r3" x="475.8" y="581.2" textLength="36.6" clip-path="url(#terminal-2151679122-line-23)">...</text><text class="terminal-2151679122-r2" x="512.4" y="581.2" textLength="12.2" clip-path="url(#terminal-2151679122-line-23)">]</text><text class="terminal-2151679122-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-2151679122-line-23)"> +</text><text class="terminal-2151679122-r1" x="0" y="605.6" textLength="951.6" clip-path="url(#terminal-2151679122-line-24)">                        allowed commit message prefixes. If the message starts</text><text class="terminal-2151679122-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-2151679122-line-24)"> +</text><text class="terminal-2151679122-r1" x="0" y="630" textLength="951.6" clip-path="url(#terminal-2151679122-line-25)">                        by one of these prefixes, the message won't be checked</text><text class="terminal-2151679122-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-2151679122-line-25)"> +</text><text class="terminal-2151679122-r1" x="0" y="654.4" textLength="500.2" clip-path="url(#terminal-2151679122-line-26)">                        against the regex</text><text class="terminal-2151679122-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-2151679122-line-26)"> +</text><text class="terminal-2151679122-r1" x="0" y="678.8" textLength="597.8" clip-path="url(#terminal-2151679122-line-27)">  -l, --message-length-limit MESSAGE_LENGTH_LIMIT</text><text class="terminal-2151679122-r1" x="976" y="678.8" textLength="12.2" clip-path="url(#terminal-2151679122-line-27)"> +</text><text class="terminal-2151679122-r1" x="0" y="703.2" textLength="732" clip-path="url(#terminal-2151679122-line-28)">                        length limit of the commit message; </text><text class="terminal-2151679122-r4" x="732" y="703.2" textLength="12.2" clip-path="url(#terminal-2151679122-line-28)">0</text><text class="terminal-2151679122-r1" x="744.2" y="703.2" textLength="158.6" clip-path="url(#terminal-2151679122-line-28)"> for no limit</text><text class="terminal-2151679122-r1" x="976" y="703.2" textLength="12.2" clip-path="url(#terminal-2151679122-line-28)"> +</text><text class="terminal-2151679122-r1" x="976" y="727.6" textLength="12.2" clip-path="url(#terminal-2151679122-line-29)"> </text> </g> </g> diff --git a/docs/images/cli_help/cz_commit___help.svg b/docs/images/cli_help/cz_commit___help.svg index 5aea02232f..59cdd12b9b 100644 --- a/docs/images/cli_help/cz_commit___help.svg +++ b/docs/images/cli_help/cz_commit___help.svg @@ -19,95 +19,96 @@ font-weight: 700; } - .terminal-463778956-matrix { + .terminal-905765158-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-463778956-title { + .terminal-905765158-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-463778956-r1 { fill: #c5c8c6 } -.terminal-463778956-r2 { fill: #c5c8c6;font-weight: bold } -.terminal-463778956-r3 { fill: #68a0b3;font-weight: bold } + .terminal-905765158-r1 { fill: #c5c8c6 } +.terminal-905765158-r2 { fill: #c5c8c6;font-weight: bold } +.terminal-905765158-r3 { fill: #98a84b } +.terminal-905765158-r4 { fill: #68a0b3;font-weight: bold } </style> <defs> - <clipPath id="terminal-463778956-clip-terminal"> + <clipPath id="terminal-905765158-clip-terminal"> <rect x="0" y="0" width="975.0" height="584.5999999999999" /> </clipPath> - <clipPath id="terminal-463778956-line-0"> + <clipPath id="terminal-905765158-line-0"> <rect x="0" y="1.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-1"> +<clipPath id="terminal-905765158-line-1"> <rect x="0" y="25.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-2"> +<clipPath id="terminal-905765158-line-2"> <rect x="0" y="50.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-3"> +<clipPath id="terminal-905765158-line-3"> <rect x="0" y="74.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-4"> +<clipPath id="terminal-905765158-line-4"> <rect x="0" y="99.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-5"> +<clipPath id="terminal-905765158-line-5"> <rect x="0" y="123.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-6"> +<clipPath id="terminal-905765158-line-6"> <rect x="0" y="147.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-7"> +<clipPath id="terminal-905765158-line-7"> <rect x="0" y="172.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-8"> +<clipPath id="terminal-905765158-line-8"> <rect x="0" y="196.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-9"> +<clipPath id="terminal-905765158-line-9"> <rect x="0" y="221.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-10"> +<clipPath id="terminal-905765158-line-10"> <rect x="0" y="245.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-11"> +<clipPath id="terminal-905765158-line-11"> <rect x="0" y="269.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-12"> +<clipPath id="terminal-905765158-line-12"> <rect x="0" y="294.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-13"> +<clipPath id="terminal-905765158-line-13"> <rect x="0" y="318.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-14"> +<clipPath id="terminal-905765158-line-14"> <rect x="0" y="343.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-15"> +<clipPath id="terminal-905765158-line-15"> <rect x="0" y="367.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-16"> +<clipPath id="terminal-905765158-line-16"> <rect x="0" y="391.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-17"> +<clipPath id="terminal-905765158-line-17"> <rect x="0" y="416.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-18"> +<clipPath id="terminal-905765158-line-18"> <rect x="0" y="440.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-19"> +<clipPath id="terminal-905765158-line-19"> <rect x="0" y="465.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-20"> +<clipPath id="terminal-905765158-line-20"> <rect x="0" y="489.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-21"> +<clipPath id="terminal-905765158-line-21"> <rect x="0" y="513.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-463778956-line-22"> +<clipPath id="terminal-905765158-line-22"> <rect x="0" y="538.3" width="976" height="24.65"/> </clipPath> </defs> @@ -119,33 +120,33 @@ <circle cx="44" cy="0" r="7" fill="#28c840"/> </g> - <g transform="translate(9, 41)" clip-path="url(#terminal-463778956-clip-terminal)"> + <g transform="translate(9, 41)" clip-path="url(#terminal-905765158-clip-terminal)"> - <g class="terminal-463778956-matrix"> - <text class="terminal-463778956-r1" x="0" y="20" textLength="219.6" clip-path="url(#terminal-463778956-line-0)">$ cz commit --help</text><text class="terminal-463778956-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-463778956-line-0)"> -</text><text class="terminal-463778956-r1" x="0" y="44.4" textLength="207.4" clip-path="url(#terminal-463778956-line-1)">usage: cz commit </text><text class="terminal-463778956-r2" x="207.4" y="44.4" textLength="12.2" clip-path="url(#terminal-463778956-line-1)">[</text><text class="terminal-463778956-r1" x="219.6" y="44.4" textLength="24.4" clip-path="url(#terminal-463778956-line-1)">-h</text><text class="terminal-463778956-r2" x="244" y="44.4" textLength="12.2" clip-path="url(#terminal-463778956-line-1)">]</text><text class="terminal-463778956-r2" x="268.4" y="44.4" textLength="12.2" clip-path="url(#terminal-463778956-line-1)">[</text><text class="terminal-463778956-r1" x="280.6" y="44.4" textLength="85.4" clip-path="url(#terminal-463778956-line-1)">--retry</text><text class="terminal-463778956-r2" x="366" y="44.4" textLength="12.2" clip-path="url(#terminal-463778956-line-1)">]</text><text class="terminal-463778956-r2" x="390.4" y="44.4" textLength="12.2" clip-path="url(#terminal-463778956-line-1)">[</text><text class="terminal-463778956-r1" x="402.6" y="44.4" textLength="122" clip-path="url(#terminal-463778956-line-1)">--no-retry</text><text class="terminal-463778956-r2" x="524.6" y="44.4" textLength="12.2" clip-path="url(#terminal-463778956-line-1)">]</text><text class="terminal-463778956-r2" x="549" y="44.4" textLength="12.2" clip-path="url(#terminal-463778956-line-1)">[</text><text class="terminal-463778956-r1" x="561.2" y="44.4" textLength="109.8" clip-path="url(#terminal-463778956-line-1)">--dry-run</text><text class="terminal-463778956-r2" x="671" y="44.4" textLength="12.2" clip-path="url(#terminal-463778956-line-1)">]</text><text class="terminal-463778956-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-463778956-line-1)"> -</text><text class="terminal-463778956-r2" x="207.4" y="68.8" textLength="12.2" clip-path="url(#terminal-463778956-line-2)">[</text><text class="terminal-463778956-r1" x="219.6" y="68.8" textLength="402.6" clip-path="url(#terminal-463778956-line-2)">--write-message-to-file FILE_PATH</text><text class="terminal-463778956-r2" x="622.2" y="68.8" textLength="12.2" clip-path="url(#terminal-463778956-line-2)">]</text><text class="terminal-463778956-r2" x="646.6" y="68.8" textLength="12.2" clip-path="url(#terminal-463778956-line-2)">[</text><text class="terminal-463778956-r1" x="658.8" y="68.8" textLength="24.4" clip-path="url(#terminal-463778956-line-2)">-s</text><text class="terminal-463778956-r2" x="683.2" y="68.8" textLength="12.2" clip-path="url(#terminal-463778956-line-2)">]</text><text class="terminal-463778956-r2" x="707.6" y="68.8" textLength="12.2" clip-path="url(#terminal-463778956-line-2)">[</text><text class="terminal-463778956-r1" x="719.8" y="68.8" textLength="24.4" clip-path="url(#terminal-463778956-line-2)">-a</text><text class="terminal-463778956-r2" x="744.2" y="68.8" textLength="12.2" clip-path="url(#terminal-463778956-line-2)">]</text><text class="terminal-463778956-r2" x="768.6" y="68.8" textLength="12.2" clip-path="url(#terminal-463778956-line-2)">[</text><text class="terminal-463778956-r1" x="780.8" y="68.8" textLength="24.4" clip-path="url(#terminal-463778956-line-2)">-e</text><text class="terminal-463778956-r2" x="805.2" y="68.8" textLength="12.2" clip-path="url(#terminal-463778956-line-2)">]</text><text class="terminal-463778956-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-463778956-line-2)"> -</text><text class="terminal-463778956-r2" x="207.4" y="93.2" textLength="12.2" clip-path="url(#terminal-463778956-line-3)">[</text><text class="terminal-463778956-r1" x="219.6" y="93.2" textLength="280.6" clip-path="url(#terminal-463778956-line-3)">-l MESSAGE_LENGTH_LIMIT</text><text class="terminal-463778956-r2" x="500.2" y="93.2" textLength="12.2" clip-path="url(#terminal-463778956-line-3)">]</text><text class="terminal-463778956-r2" x="524.6" y="93.2" textLength="12.2" clip-path="url(#terminal-463778956-line-3)">[</text><text class="terminal-463778956-r1" x="536.8" y="93.2" textLength="24.4" clip-path="url(#terminal-463778956-line-3)">--</text><text class="terminal-463778956-r2" x="561.2" y="93.2" textLength="12.2" clip-path="url(#terminal-463778956-line-3)">]</text><text class="terminal-463778956-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-463778956-line-3)"> -</text><text class="terminal-463778956-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-463778956-line-4)"> -</text><text class="terminal-463778956-r1" x="0" y="142" textLength="207.4" clip-path="url(#terminal-463778956-line-5)">create new commit</text><text class="terminal-463778956-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-463778956-line-5)"> -</text><text class="terminal-463778956-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-463778956-line-6)"> -</text><text class="terminal-463778956-r1" x="0" y="190.8" textLength="97.6" clip-path="url(#terminal-463778956-line-7)">options:</text><text class="terminal-463778956-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-463778956-line-7)"> -</text><text class="terminal-463778956-r1" x="0" y="215.2" textLength="671" clip-path="url(#terminal-463778956-line-8)">  -h, --help            show this help message and exit</text><text class="terminal-463778956-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-463778956-line-8)"> -</text><text class="terminal-463778956-r1" x="0" y="239.6" textLength="500.2" clip-path="url(#terminal-463778956-line-9)">  --retry               retry last commit</text><text class="terminal-463778956-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-463778956-line-9)"> -</text><text class="terminal-463778956-r1" x="0" y="264" textLength="878.4" clip-path="url(#terminal-463778956-line-10)">  --no-retry            skip retry if retry_after_failure is set to true</text><text class="terminal-463778956-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-463778956-line-10)"> -</text><text class="terminal-463778956-r1" x="0" y="288.4" textLength="915" clip-path="url(#terminal-463778956-line-11)">  --dry-run             show output to stdout, no commit, no modified files</text><text class="terminal-463778956-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-463778956-line-11)"> -</text><text class="terminal-463778956-r1" x="0" y="312.8" textLength="427" clip-path="url(#terminal-463778956-line-12)">  --write-message-to-file FILE_PATH</text><text class="terminal-463778956-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-463778956-line-12)"> -</text><text class="terminal-463778956-r1" x="0" y="337.2" textLength="780.8" clip-path="url(#terminal-463778956-line-13)">                        write message to file before committing </text><text class="terminal-463778956-r2" x="780.8" y="337.2" textLength="12.2" clip-path="url(#terminal-463778956-line-13)">(</text><text class="terminal-463778956-r1" x="793" y="337.2" textLength="73.2" clip-path="url(#terminal-463778956-line-13)">can be</text><text class="terminal-463778956-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-463778956-line-13)"> -</text><text class="terminal-463778956-r1" x="0" y="361.6" textLength="573.4" clip-path="url(#terminal-463778956-line-14)">                        combined with --dry-run</text><text class="terminal-463778956-r2" x="573.4" y="361.6" textLength="12.2" clip-path="url(#terminal-463778956-line-14)">)</text><text class="terminal-463778956-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-463778956-line-14)"> -</text><text class="terminal-463778956-r1" x="0" y="386" textLength="524.6" clip-path="url(#terminal-463778956-line-15)">  -s, --signoff         sign off the commit</text><text class="terminal-463778956-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-463778956-line-15)"> -</text><text class="terminal-463778956-r1" x="0" y="410.4" textLength="902.8" clip-path="url(#terminal-463778956-line-16)">  -a, --all             Tell the command to automatically stage files that</text><text class="terminal-463778956-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-463778956-line-16)"> -</text><text class="terminal-463778956-r1" x="0" y="434.8" textLength="951.6" clip-path="url(#terminal-463778956-line-17)">                        have been modified and deleted, but new files you have</text><text class="terminal-463778956-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-463778956-line-17)"> -</text><text class="terminal-463778956-r1" x="0" y="459.2" textLength="732" clip-path="url(#terminal-463778956-line-18)">                        not told Git about are not affected.</text><text class="terminal-463778956-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-463778956-line-18)"> -</text><text class="terminal-463778956-r1" x="0" y="483.6" textLength="793" clip-path="url(#terminal-463778956-line-19)">  -e, --edit            edit the commit message before committing</text><text class="terminal-463778956-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-463778956-line-19)"> -</text><text class="terminal-463778956-r1" x="0" y="508" textLength="597.8" clip-path="url(#terminal-463778956-line-20)">  -l, --message-length-limit MESSAGE_LENGTH_LIMIT</text><text class="terminal-463778956-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-463778956-line-20)"> -</text><text class="terminal-463778956-r1" x="0" y="532.4" textLength="732" clip-path="url(#terminal-463778956-line-21)">                        length limit of the commit message; </text><text class="terminal-463778956-r3" x="732" y="532.4" textLength="12.2" clip-path="url(#terminal-463778956-line-21)">0</text><text class="terminal-463778956-r1" x="744.2" y="532.4" textLength="158.6" clip-path="url(#terminal-463778956-line-21)"> for no limit</text><text class="terminal-463778956-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-463778956-line-21)"> -</text><text class="terminal-463778956-r1" x="0" y="556.8" textLength="671" clip-path="url(#terminal-463778956-line-22)">  --                    Positional arguments separator </text><text class="terminal-463778956-r2" x="671" y="556.8" textLength="12.2" clip-path="url(#terminal-463778956-line-22)">(</text><text class="terminal-463778956-r1" x="683.2" y="556.8" textLength="134.2" clip-path="url(#terminal-463778956-line-22)">recommended</text><text class="terminal-463778956-r2" x="817.4" y="556.8" textLength="12.2" clip-path="url(#terminal-463778956-line-22)">)</text><text class="terminal-463778956-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-463778956-line-22)"> -</text><text class="terminal-463778956-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-463778956-line-23)"> + <g class="terminal-905765158-matrix"> + <text class="terminal-905765158-r1" x="0" y="20" textLength="219.6" clip-path="url(#terminal-905765158-line-0)">$ cz commit --help</text><text class="terminal-905765158-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-905765158-line-0)"> +</text><text class="terminal-905765158-r1" x="0" y="44.4" textLength="207.4" clip-path="url(#terminal-905765158-line-1)">usage: cz commit </text><text class="terminal-905765158-r2" x="207.4" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">[</text><text class="terminal-905765158-r1" x="219.6" y="44.4" textLength="24.4" clip-path="url(#terminal-905765158-line-1)">-h</text><text class="terminal-905765158-r2" x="244" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">]</text><text class="terminal-905765158-r2" x="268.4" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">[</text><text class="terminal-905765158-r1" x="280.6" y="44.4" textLength="85.4" clip-path="url(#terminal-905765158-line-1)">--retry</text><text class="terminal-905765158-r2" x="366" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">]</text><text class="terminal-905765158-r2" x="390.4" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">[</text><text class="terminal-905765158-r1" x="402.6" y="44.4" textLength="122" clip-path="url(#terminal-905765158-line-1)">--no-retry</text><text class="terminal-905765158-r2" x="524.6" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">]</text><text class="terminal-905765158-r2" x="549" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">[</text><text class="terminal-905765158-r1" x="561.2" y="44.4" textLength="109.8" clip-path="url(#terminal-905765158-line-1)">--dry-run</text><text class="terminal-905765158-r2" x="671" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">]</text><text class="terminal-905765158-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)"> +</text><text class="terminal-905765158-r2" x="207.4" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">[</text><text class="terminal-905765158-r1" x="219.6" y="68.8" textLength="402.6" clip-path="url(#terminal-905765158-line-2)">--write-message-to-file FILE_PATH</text><text class="terminal-905765158-r2" x="622.2" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">]</text><text class="terminal-905765158-r2" x="646.6" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">[</text><text class="terminal-905765158-r1" x="658.8" y="68.8" textLength="24.4" clip-path="url(#terminal-905765158-line-2)">-s</text><text class="terminal-905765158-r2" x="683.2" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">]</text><text class="terminal-905765158-r2" x="707.6" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">[</text><text class="terminal-905765158-r1" x="719.8" y="68.8" textLength="24.4" clip-path="url(#terminal-905765158-line-2)">-a</text><text class="terminal-905765158-r2" x="744.2" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">]</text><text class="terminal-905765158-r2" x="768.6" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">[</text><text class="terminal-905765158-r1" x="780.8" y="68.8" textLength="24.4" clip-path="url(#terminal-905765158-line-2)">-e</text><text class="terminal-905765158-r2" x="805.2" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">]</text><text class="terminal-905765158-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)"> +</text><text class="terminal-905765158-r2" x="207.4" y="93.2" textLength="12.2" clip-path="url(#terminal-905765158-line-3)">[</text><text class="terminal-905765158-r1" x="219.6" y="93.2" textLength="280.6" clip-path="url(#terminal-905765158-line-3)">-l MESSAGE_LENGTH_LIMIT</text><text class="terminal-905765158-r2" x="500.2" y="93.2" textLength="12.2" clip-path="url(#terminal-905765158-line-3)">]</text><text class="terminal-905765158-r2" x="524.6" y="93.2" textLength="12.2" clip-path="url(#terminal-905765158-line-3)">[</text><text class="terminal-905765158-r1" x="536.8" y="93.2" textLength="24.4" clip-path="url(#terminal-905765158-line-3)">--</text><text class="terminal-905765158-r2" x="561.2" y="93.2" textLength="12.2" clip-path="url(#terminal-905765158-line-3)">]</text><text class="terminal-905765158-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-905765158-line-3)"> +</text><text class="terminal-905765158-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-905765158-line-4)"> +</text><text class="terminal-905765158-r1" x="0" y="142" textLength="207.4" clip-path="url(#terminal-905765158-line-5)">create new commit</text><text class="terminal-905765158-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-905765158-line-5)"> +</text><text class="terminal-905765158-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-905765158-line-6)"> +</text><text class="terminal-905765158-r1" x="0" y="190.8" textLength="97.6" clip-path="url(#terminal-905765158-line-7)">options:</text><text class="terminal-905765158-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-905765158-line-7)"> +</text><text class="terminal-905765158-r1" x="0" y="215.2" textLength="671" clip-path="url(#terminal-905765158-line-8)">  -h, --help            show this help message and exit</text><text class="terminal-905765158-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-905765158-line-8)"> +</text><text class="terminal-905765158-r1" x="0" y="239.6" textLength="500.2" clip-path="url(#terminal-905765158-line-9)">  --retry               retry last commit</text><text class="terminal-905765158-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-905765158-line-9)"> +</text><text class="terminal-905765158-r1" x="0" y="264" textLength="878.4" clip-path="url(#terminal-905765158-line-10)">  --no-retry            skip retry if retry_after_failure is set to true</text><text class="terminal-905765158-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-905765158-line-10)"> +</text><text class="terminal-905765158-r1" x="0" y="288.4" textLength="915" clip-path="url(#terminal-905765158-line-11)">  --dry-run             show output to stdout, no commit, no modified files</text><text class="terminal-905765158-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-905765158-line-11)"> +</text><text class="terminal-905765158-r1" x="0" y="312.8" textLength="427" clip-path="url(#terminal-905765158-line-12)">  --write-message-to-file FILE_PATH</text><text class="terminal-905765158-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-905765158-line-12)"> +</text><text class="terminal-905765158-r1" x="0" y="337.2" textLength="780.8" clip-path="url(#terminal-905765158-line-13)">                        write message to file before committing </text><text class="terminal-905765158-r2" x="780.8" y="337.2" textLength="12.2" clip-path="url(#terminal-905765158-line-13)">(</text><text class="terminal-905765158-r1" x="793" y="337.2" textLength="73.2" clip-path="url(#terminal-905765158-line-13)">can be</text><text class="terminal-905765158-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-905765158-line-13)"> +</text><text class="terminal-905765158-r1" x="0" y="361.6" textLength="573.4" clip-path="url(#terminal-905765158-line-14)">                        combined with --dry-run</text><text class="terminal-905765158-r2" x="573.4" y="361.6" textLength="12.2" clip-path="url(#terminal-905765158-line-14)">)</text><text class="terminal-905765158-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-905765158-line-14)"> +</text><text class="terminal-905765158-r1" x="0" y="386" textLength="488" clip-path="url(#terminal-905765158-line-15)">  -s, --signoff         Deprecated, use </text><text class="terminal-905765158-r3" x="488" y="386" textLength="207.4" clip-path="url(#terminal-905765158-line-15)">'cz commit -- -s'</text><text class="terminal-905765158-r1" x="695.4" y="386" textLength="97.6" clip-path="url(#terminal-905765158-line-15)"> instead</text><text class="terminal-905765158-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-905765158-line-15)"> +</text><text class="terminal-905765158-r1" x="0" y="410.4" textLength="902.8" clip-path="url(#terminal-905765158-line-16)">  -a, --all             Tell the command to automatically stage files that</text><text class="terminal-905765158-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-905765158-line-16)"> +</text><text class="terminal-905765158-r1" x="0" y="434.8" textLength="951.6" clip-path="url(#terminal-905765158-line-17)">                        have been modified and deleted, but new files you have</text><text class="terminal-905765158-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-905765158-line-17)"> +</text><text class="terminal-905765158-r1" x="0" y="459.2" textLength="732" clip-path="url(#terminal-905765158-line-18)">                        not told Git about are not affected.</text><text class="terminal-905765158-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-905765158-line-18)"> +</text><text class="terminal-905765158-r1" x="0" y="483.6" textLength="793" clip-path="url(#terminal-905765158-line-19)">  -e, --edit            edit the commit message before committing</text><text class="terminal-905765158-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-905765158-line-19)"> +</text><text class="terminal-905765158-r1" x="0" y="508" textLength="597.8" clip-path="url(#terminal-905765158-line-20)">  -l, --message-length-limit MESSAGE_LENGTH_LIMIT</text><text class="terminal-905765158-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-905765158-line-20)"> +</text><text class="terminal-905765158-r1" x="0" y="532.4" textLength="732" clip-path="url(#terminal-905765158-line-21)">                        length limit of the commit message; </text><text class="terminal-905765158-r4" x="732" y="532.4" textLength="12.2" clip-path="url(#terminal-905765158-line-21)">0</text><text class="terminal-905765158-r1" x="744.2" y="532.4" textLength="158.6" clip-path="url(#terminal-905765158-line-21)"> for no limit</text><text class="terminal-905765158-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-905765158-line-21)"> +</text><text class="terminal-905765158-r1" x="0" y="556.8" textLength="671" clip-path="url(#terminal-905765158-line-22)">  --                    Positional arguments separator </text><text class="terminal-905765158-r2" x="671" y="556.8" textLength="12.2" clip-path="url(#terminal-905765158-line-22)">(</text><text class="terminal-905765158-r1" x="683.2" y="556.8" textLength="134.2" clip-path="url(#terminal-905765158-line-22)">recommended</text><text class="terminal-905765158-r2" x="817.4" y="556.8" textLength="12.2" clip-path="url(#terminal-905765158-line-22)">)</text><text class="terminal-905765158-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-905765158-line-22)"> +</text><text class="terminal-905765158-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-905765158-line-23)"> </text> </g> </g> diff --git a/docs/images/cli_help/cz_version___help.svg b/docs/images/cli_help/cz_version___help.svg index c7777db4df..a8e0bd844e 100644 --- a/docs/images/cli_help/cz_version___help.svg +++ b/docs/images/cli_help/cz_version___help.svg @@ -1,4 +1,4 @@ -<svg class="rich-terminal" viewBox="0 0 994 391.59999999999997" xmlns="http://www.w3.org/2000/svg"> +<svg class="rich-terminal" viewBox="0 0 994 489.2" xmlns="http://www.w3.org/2000/svg"> <!-- Generated with Rich https://www.textualize.io --> <style> @@ -19,92 +19,108 @@ font-weight: 700; } - .terminal-4023877003-matrix { + .terminal-752487608-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-4023877003-title { + .terminal-752487608-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-4023877003-r1 { fill: #c5c8c6 } -.terminal-4023877003-r2 { fill: #c5c8c6;font-weight: bold } + .terminal-752487608-r1 { fill: #c5c8c6 } +.terminal-752487608-r2 { fill: #c5c8c6;font-weight: bold } </style> <defs> - <clipPath id="terminal-4023877003-clip-terminal"> - <rect x="0" y="0" width="975.0" height="340.59999999999997" /> + <clipPath id="terminal-752487608-clip-terminal"> + <rect x="0" y="0" width="975.0" height="438.2" /> </clipPath> - <clipPath id="terminal-4023877003-line-0"> + <clipPath id="terminal-752487608-line-0"> <rect x="0" y="1.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-4023877003-line-1"> +<clipPath id="terminal-752487608-line-1"> <rect x="0" y="25.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-4023877003-line-2"> +<clipPath id="terminal-752487608-line-2"> <rect x="0" y="50.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-4023877003-line-3"> +<clipPath id="terminal-752487608-line-3"> <rect x="0" y="74.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-4023877003-line-4"> +<clipPath id="terminal-752487608-line-4"> <rect x="0" y="99.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-4023877003-line-5"> +<clipPath id="terminal-752487608-line-5"> <rect x="0" y="123.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-4023877003-line-6"> +<clipPath id="terminal-752487608-line-6"> <rect x="0" y="147.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-4023877003-line-7"> +<clipPath id="terminal-752487608-line-7"> <rect x="0" y="172.3" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-4023877003-line-8"> +<clipPath id="terminal-752487608-line-8"> <rect x="0" y="196.7" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-4023877003-line-9"> +<clipPath id="terminal-752487608-line-9"> <rect x="0" y="221.1" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-4023877003-line-10"> +<clipPath id="terminal-752487608-line-10"> <rect x="0" y="245.5" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-4023877003-line-11"> +<clipPath id="terminal-752487608-line-11"> <rect x="0" y="269.9" width="976" height="24.65"/> </clipPath> -<clipPath id="terminal-4023877003-line-12"> +<clipPath id="terminal-752487608-line-12"> <rect x="0" y="294.3" width="976" height="24.65"/> </clipPath> +<clipPath id="terminal-752487608-line-13"> + <rect x="0" y="318.7" width="976" height="24.65"/> + </clipPath> +<clipPath id="terminal-752487608-line-14"> + <rect x="0" y="343.1" width="976" height="24.65"/> + </clipPath> +<clipPath id="terminal-752487608-line-15"> + <rect x="0" y="367.5" width="976" height="24.65"/> + </clipPath> +<clipPath id="terminal-752487608-line-16"> + <rect x="0" y="391.9" width="976" height="24.65"/> + </clipPath> </defs> - <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="389.6" rx="8"/> + <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="487.2" rx="8"/> <g transform="translate(26,22)"> <circle cx="0" cy="0" r="7" fill="#ff5f57"/> <circle cx="22" cy="0" r="7" fill="#febc2e"/> <circle cx="44" cy="0" r="7" fill="#28c840"/> </g> - <g transform="translate(9, 41)" clip-path="url(#terminal-4023877003-clip-terminal)"> + <g transform="translate(9, 41)" clip-path="url(#terminal-752487608-clip-terminal)"> - <g class="terminal-4023877003-matrix"> - <text class="terminal-4023877003-r1" x="0" y="20" textLength="231.8" clip-path="url(#terminal-4023877003-line-0)">$ cz version --help</text><text class="terminal-4023877003-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-4023877003-line-0)"> -</text><text class="terminal-4023877003-r1" x="0" y="44.4" textLength="219.6" clip-path="url(#terminal-4023877003-line-1)">usage: cz version </text><text class="terminal-4023877003-r2" x="219.6" y="44.4" textLength="12.2" clip-path="url(#terminal-4023877003-line-1)">[</text><text class="terminal-4023877003-r1" x="231.8" y="44.4" textLength="24.4" clip-path="url(#terminal-4023877003-line-1)">-h</text><text class="terminal-4023877003-r2" x="256.2" y="44.4" textLength="12.2" clip-path="url(#terminal-4023877003-line-1)">]</text><text class="terminal-4023877003-r2" x="280.6" y="44.4" textLength="12.2" clip-path="url(#terminal-4023877003-line-1)">[</text><text class="terminal-4023877003-r1" x="292.8" y="44.4" textLength="207.4" clip-path="url(#terminal-4023877003-line-1)">-r | -p | -c | -v</text><text class="terminal-4023877003-r2" x="500.2" y="44.4" textLength="12.2" clip-path="url(#terminal-4023877003-line-1)">]</text><text class="terminal-4023877003-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-4023877003-line-1)"> -</text><text class="terminal-4023877003-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-4023877003-line-2)"> -</text><text class="terminal-4023877003-r1" x="0" y="93.2" textLength="817.4" clip-path="url(#terminal-4023877003-line-3)">get the version of the installed commitizen or the current project </text><text class="terminal-4023877003-r2" x="817.4" y="93.2" textLength="12.2" clip-path="url(#terminal-4023877003-line-3)">(</text><text class="terminal-4023877003-r1" x="829.6" y="93.2" textLength="97.6" clip-path="url(#terminal-4023877003-line-3)">default:</text><text class="terminal-4023877003-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-4023877003-line-3)"> -</text><text class="terminal-4023877003-r1" x="0" y="117.6" textLength="244" clip-path="url(#terminal-4023877003-line-4)">installed commitizen</text><text class="terminal-4023877003-r2" x="244" y="117.6" textLength="12.2" clip-path="url(#terminal-4023877003-line-4)">)</text><text class="terminal-4023877003-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-4023877003-line-4)"> -</text><text class="terminal-4023877003-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-4023877003-line-5)"> -</text><text class="terminal-4023877003-r1" x="0" y="166.4" textLength="97.6" clip-path="url(#terminal-4023877003-line-6)">options:</text><text class="terminal-4023877003-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-4023877003-line-6)"> -</text><text class="terminal-4023877003-r1" x="0" y="190.8" textLength="622.2" clip-path="url(#terminal-4023877003-line-7)">  -h, --help        show this help message and exit</text><text class="terminal-4023877003-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-4023877003-line-7)"> -</text><text class="terminal-4023877003-r1" x="0" y="215.2" textLength="744.2" clip-path="url(#terminal-4023877003-line-8)">  -r, --report      get system information for reporting bugs</text><text class="terminal-4023877003-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-4023877003-line-8)"> -</text><text class="terminal-4023877003-r1" x="0" y="239.6" textLength="707.6" clip-path="url(#terminal-4023877003-line-9)">  -p, --project     get the version of the current project</text><text class="terminal-4023877003-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-4023877003-line-9)"> -</text><text class="terminal-4023877003-r1" x="0" y="264" textLength="768.6" clip-path="url(#terminal-4023877003-line-10)">  -c, --commitizen  get the version of the installed commitizen</text><text class="terminal-4023877003-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-4023877003-line-10)"> -</text><text class="terminal-4023877003-r1" x="0" y="288.4" textLength="927.2" clip-path="url(#terminal-4023877003-line-11)">  -v, --verbose     get the version of both the installed commitizen and the</text><text class="terminal-4023877003-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-4023877003-line-11)"> -</text><text class="terminal-4023877003-r1" x="0" y="312.8" textLength="427" clip-path="url(#terminal-4023877003-line-12)">                    current project</text><text class="terminal-4023877003-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-4023877003-line-12)"> -</text><text class="terminal-4023877003-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-4023877003-line-13)"> + <g class="terminal-752487608-matrix"> + <text class="terminal-752487608-r1" x="0" y="20" textLength="231.8" clip-path="url(#terminal-752487608-line-0)">$ cz version --help</text><text class="terminal-752487608-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-752487608-line-0)"> +</text><text class="terminal-752487608-r1" x="0" y="44.4" textLength="219.6" clip-path="url(#terminal-752487608-line-1)">usage: cz version </text><text class="terminal-752487608-r2" x="219.6" y="44.4" textLength="12.2" clip-path="url(#terminal-752487608-line-1)">[</text><text class="terminal-752487608-r1" x="231.8" y="44.4" textLength="24.4" clip-path="url(#terminal-752487608-line-1)">-h</text><text class="terminal-752487608-r2" x="256.2" y="44.4" textLength="12.2" clip-path="url(#terminal-752487608-line-1)">]</text><text class="terminal-752487608-r2" x="280.6" y="44.4" textLength="12.2" clip-path="url(#terminal-752487608-line-1)">[</text><text class="terminal-752487608-r1" x="292.8" y="44.4" textLength="207.4" clip-path="url(#terminal-752487608-line-1)">-r | -p | -c | -v</text><text class="terminal-752487608-r2" x="500.2" y="44.4" textLength="12.2" clip-path="url(#terminal-752487608-line-1)">]</text><text class="terminal-752487608-r2" x="524.6" y="44.4" textLength="12.2" clip-path="url(#terminal-752487608-line-1)">[</text><text class="terminal-752487608-r1" x="536.8" y="44.4" textLength="207.4" clip-path="url(#terminal-752487608-line-1)">--major | --minor</text><text class="terminal-752487608-r2" x="744.2" y="44.4" textLength="12.2" clip-path="url(#terminal-752487608-line-1)">]</text><text class="terminal-752487608-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-752487608-line-1)"> +</text><text class="terminal-752487608-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-752487608-line-2)"> +</text><text class="terminal-752487608-r1" x="0" y="93.2" textLength="817.4" clip-path="url(#terminal-752487608-line-3)">get the version of the installed commitizen or the current project </text><text class="terminal-752487608-r2" x="817.4" y="93.2" textLength="12.2" clip-path="url(#terminal-752487608-line-3)">(</text><text class="terminal-752487608-r1" x="829.6" y="93.2" textLength="97.6" clip-path="url(#terminal-752487608-line-3)">default:</text><text class="terminal-752487608-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-752487608-line-3)"> +</text><text class="terminal-752487608-r1" x="0" y="117.6" textLength="244" clip-path="url(#terminal-752487608-line-4)">installed commitizen</text><text class="terminal-752487608-r2" x="244" y="117.6" textLength="12.2" clip-path="url(#terminal-752487608-line-4)">)</text><text class="terminal-752487608-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-752487608-line-4)"> +</text><text class="terminal-752487608-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-752487608-line-5)"> +</text><text class="terminal-752487608-r1" x="0" y="166.4" textLength="97.6" clip-path="url(#terminal-752487608-line-6)">options:</text><text class="terminal-752487608-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-752487608-line-6)"> +</text><text class="terminal-752487608-r1" x="0" y="190.8" textLength="622.2" clip-path="url(#terminal-752487608-line-7)">  -h, --help        show this help message and exit</text><text class="terminal-752487608-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-752487608-line-7)"> +</text><text class="terminal-752487608-r1" x="0" y="215.2" textLength="744.2" clip-path="url(#terminal-752487608-line-8)">  -r, --report      get system information for reporting bugs</text><text class="terminal-752487608-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-752487608-line-8)"> +</text><text class="terminal-752487608-r1" x="0" y="239.6" textLength="707.6" clip-path="url(#terminal-752487608-line-9)">  -p, --project     get the version of the current project</text><text class="terminal-752487608-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-752487608-line-9)"> +</text><text class="terminal-752487608-r1" x="0" y="264" textLength="768.6" clip-path="url(#terminal-752487608-line-10)">  -c, --commitizen  get the version of the installed commitizen</text><text class="terminal-752487608-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-752487608-line-10)"> +</text><text class="terminal-752487608-r1" x="0" y="288.4" textLength="927.2" clip-path="url(#terminal-752487608-line-11)">  -v, --verbose     get the version of both the installed commitizen and the</text><text class="terminal-752487608-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-752487608-line-11)"> +</text><text class="terminal-752487608-r1" x="0" y="312.8" textLength="427" clip-path="url(#terminal-752487608-line-12)">                    current project</text><text class="terminal-752487608-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-752487608-line-12)"> +</text><text class="terminal-752487608-r1" x="0" y="337.2" textLength="951.6" clip-path="url(#terminal-752487608-line-13)">  --major           get just the major version. Need to be used with --project</text><text class="terminal-752487608-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-752487608-line-13)"> +</text><text class="terminal-752487608-r1" x="0" y="361.6" textLength="402.6" clip-path="url(#terminal-752487608-line-14)">                    or --verbose.</text><text class="terminal-752487608-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-752487608-line-14)"> +</text><text class="terminal-752487608-r1" x="0" y="386" textLength="951.6" clip-path="url(#terminal-752487608-line-15)">  --minor           get just the minor version. Need to be used with --project</text><text class="terminal-752487608-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-752487608-line-15)"> +</text><text class="terminal-752487608-r1" x="0" y="410.4" textLength="402.6" clip-path="url(#terminal-752487608-line-16)">                    or --verbose.</text><text class="terminal-752487608-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-752487608-line-16)"> +</text><text class="terminal-752487608-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-752487608-line-17)"> </text> </g> </g> diff --git a/docs/third-party-commitizen.md b/docs/third-party-commitizen.md deleted file mode 100644 index 1c8ef8a183..0000000000 --- a/docs/third-party-commitizen.md +++ /dev/null @@ -1,131 +0,0 @@ -## Third-Party Commitizen Templates - -In addition to the native templates, some alternative commit format templates -are available as PyPI packages (installable with `pip`). - -### [Conventional JIRA](https://pypi.org/project/conventional-JIRA/) - -Just like _conventional commit_ format, but the scope has been restricted to a -JIRA issue format, i.e. `project-issueNumber`. This standardises scopes in a -meaningful way. - -#### Installation - -```sh -pip install conventional-JIRA -``` - -### [GitHub JIRA Conventional](https://pypi.org/project/cz-github-jira-conventional/) - -This plugin extends the commitizen tools by: - -- requiring a JIRA issue id in the commit message -- creating links to GitHub commits in the CHANGELOG.md -- creating links to JIRA issues in the CHANGELOG.md - -#### Installation - -```sh -pip install cz-github-jira-conventional -``` - -For installation instructions (configuration and pre-commit) please visit [https://github.com/apheris/cz-github-jira-conventional](https://github.com/apheris/cz-github-jira-conventional) - -### [cz-emoji](https://github.com/adam-grant-hendry/cz-emoji) - -_conventional commit_ format, but with emojis - -#### Installation - -```sh -pip install cz-emoji -``` - -#### Usage - -```sh -cz --name cz_emoji commit -``` - -### [cz-conventional-gitmoji](https://github.com/ljnsn/cz-conventional-gitmoji) - -*conventional commit*s, but with [gitmojis](https://gitmoji.dev). - -Includes a pre-commit hook that automatically adds the correct gitmoji to the commit message based on the conventional type. - -#### Installation - -```sh -pip install cz-conventional-gitmoji -``` - -#### Usage - -```sh -cz --name cz_gitmoji commit -``` - -### [Commitizen emoji](https://pypi.org/project/commitizen-emoji/) (Unmaintained) - -Just like _conventional commit_ format, but with emojis and optionally time spent and related tasks. - -#### Installation - -```sh -pip install commitizen-emoji -``` - -#### Usage - -```sh -cz --name cz_commitizen_emoji commit -``` - -### [Conventional Legacy (cz_legacy)][1] - -An extension of the _conventional commit_ format to include user-specified -legacy change types in the `CHANGELOG` while preventing the legacy change types -from being used in new commit messages - -#### Installation - -```sh -pip install cz_legacy -``` - -#### Usage - -See the [README][1] for instructions on configuration - -[1]: https://pypi.org/project/cz_legacy - -## Third-Party Commitizen Providers - -Commitizen can read and write version from different sources. In addition to the native providers, some alternative version sources are available as PyPI packages (installable with `pip`). - -### [commitizen-deno-provider](https://pypi.org/project/commitizen-deno-provider/) - -A provider for **Deno** projects. The provider updates the version in deno.json and jsr.json files. - -#### Installation - -```sh -pip install commitizen-deno-provider -``` - -#### Usage - -Add `deno-provider` to your configuration file. - -Example for `.cz.yaml`: - -```yaml ---- -commitizen: - major_version_zero: true - name: cz_conventional_commits - tag_format: $version - update_changelog_on_bump: true - version_provider: deno-provider - version_scheme: semver -``` diff --git a/docs/third-party-plugins/about.md b/docs/third-party-plugins/about.md new file mode 100644 index 0000000000..9711386fc8 --- /dev/null +++ b/docs/third-party-plugins/about.md @@ -0,0 +1,73 @@ +# What are third-party plugins? + +Third-party plugins are a way to extend Commitizen with additional customized features. + +These plugins are available as PyPI packages (installable with `pip`). + +!!! note + New plugins are welcome! Once you published your plugins, please send us a PR to update this page. + +!!! note "Historical notes" + This section was originally called "Third-Party Commitizen Templates", but has been renamed to "Third-Party Commitizen Plugins" to better reflect the content. + +## Plugin features + +<!-- TODO: provide more details about the features --> +<!-- We can move the details from customization pages to here --> + +### Commit message convention + +Includes the rules to validate and generate commit messages. + +### Version scheme + +Includes the rules to generate version numbers. + +### Version provider + +Read and write version from data sources. + +### Changelog format + +Generate changelog in customized formats. + +<!-- TODO: complete the following section --> +<!-- ## How do I create a new plugin? --> + +## How to help us update the list of plugins? + +Please document what features the plugin provides: + +- a convention +- a scheme +- a provider +- a `changelog_format` + +Of course, a plugin can provide multiple features. +You may have noticed that `commitizen` itself can be viewed as a plugin that provides all the above features. + +Please see [cz-path](./cz-path.md) for a detailed example. + +## New plugin documentation template + + # [Package name](https://github.com/author/package-name) + + <!-- Description of the plugin. --> + + <!-- What features does the plugin provide? --> + + ## Installation + + ```sh + pip install package-name + ``` + + ## Usage + + ```sh + cz --name package-name commit + ``` + + ## Example + + <!-- Example usage of the plugin. --> diff --git a/docs/third-party-plugins/commitizen-deno-provider.md b/docs/third-party-plugins/commitizen-deno-provider.md new file mode 100644 index 0000000000..77f7d2e177 --- /dev/null +++ b/docs/third-party-plugins/commitizen-deno-provider.md @@ -0,0 +1,28 @@ +# [commitizen-deno-provider](https://pypi.org/project/commitizen-deno-provider/) + +A provider for **Deno** projects. The provider updates the version in `deno.json` and `jsr.json` files. + +<!-- TODO: What features does the plugin provide? --> + +## Installation + +```sh +pip install commitizen-deno-provider +``` + +## Usage + +Add `deno-provider` to your configuration file. + +Example for `.cz.yaml`: + +```yaml +--- +commitizen: + major_version_zero: true + name: cz_conventional_commits + tag_format: $version + update_changelog_on_bump: true + version_provider: deno-provider + version_scheme: semver +``` diff --git a/docs/third-party-plugins/commitizen-emoji.md b/docs/third-party-plugins/commitizen-emoji.md new file mode 100644 index 0000000000..e8e2654516 --- /dev/null +++ b/docs/third-party-plugins/commitizen-emoji.md @@ -0,0 +1,17 @@ +# [Commitizen emoji](https://pypi.org/project/commitizen-emoji/) (Unmaintained) + +Just like *conventional commit* format, but with emojis and optionally time spent and related tasks. + +<!-- TODO: What features does the plugin provide? --> + +## Installation + +```sh +pip install commitizen-emoji +``` + +## Usage + +```sh +cz --name cz_commitizen_emoji commit +``` diff --git a/docs/third-party-plugins/conventional-jira.md b/docs/third-party-plugins/conventional-jira.md new file mode 100644 index 0000000000..99505a2743 --- /dev/null +++ b/docs/third-party-plugins/conventional-jira.md @@ -0,0 +1,13 @@ +# [Conventional JIRA](https://pypi.org/project/conventional-JIRA/) + +Just like _conventional commit_ format, but the scope has been restricted to a +JIRA issue format, i.e. `project-issueNumber`. This standardises scopes in a +meaningful way. + +<!-- TODO: What features does the plugin provide? --> + +## Installation + +```sh +pip install conventional-JIRA +``` diff --git a/docs/third-party-plugins/cz-ai.md b/docs/third-party-plugins/cz-ai.md new file mode 100644 index 0000000000..e9ced907bf --- /dev/null +++ b/docs/third-party-plugins/cz-ai.md @@ -0,0 +1,17 @@ +# [cz-ai](https://github.com/watadarkstar/cz_ai) + +A Commitizen plugin that leverages OpenAI's GPT-4o to automatically generate clear, concise, and conventional commit messages based on your staged git changes. + +<!-- TODO: What features does the plugin provide? --> + +## Installation + +```sh +pip install cz-ai +``` + +## Usage + +```sh +cz --name cz_ai commit +``` diff --git a/docs/third-party-plugins/cz-conventional-gitmoji.md b/docs/third-party-plugins/cz-conventional-gitmoji.md new file mode 100644 index 0000000000..f4157105e7 --- /dev/null +++ b/docs/third-party-plugins/cz-conventional-gitmoji.md @@ -0,0 +1,19 @@ +# [cz-conventional-gitmoji](https://github.com/ljnsn/cz-conventional-gitmoji) + +*conventional commit*s, but with [gitmojis](https://gitmoji.dev). + +Includes a pre-commit hook that automatically adds the correct gitmoji to the commit message based on the conventional type. + +<!-- TODO: What features does the plugin provide? --> + +## Installation + +```sh +pip install cz-conventional-gitmoji +``` + +## Usage + +```sh +cz --name cz_gitmoji commit +``` diff --git a/docs/third-party-plugins/cz-emoji.md b/docs/third-party-plugins/cz-emoji.md new file mode 100644 index 0000000000..c638f4bfa1 --- /dev/null +++ b/docs/third-party-plugins/cz-emoji.md @@ -0,0 +1,17 @@ +# [cz-emoji](https://github.com/adam-grant-hendry/cz-emoji) + +_conventional commit_ format, but with emojis. + +<!-- TODO: What features does the plugin provide? --> + +## Installation + +```sh +pip install cz-emoji +``` + +## Usage + +```sh +cz --name cz_emoji commit +``` diff --git a/docs/third-party-plugins/cz-legacy.md b/docs/third-party-plugins/cz-legacy.md new file mode 100644 index 0000000000..4f0a2e1221 --- /dev/null +++ b/docs/third-party-plugins/cz-legacy.md @@ -0,0 +1,19 @@ +# [Conventional Legacy (cz_legacy)][cz_legacy] + +An extension of the *conventional commit* format to include user-specified +legacy change types in the `CHANGELOG` while preventing the legacy change types +from being used in new commit messages. + +<!-- TODO: What features does the plugin provide? --> + +## Installation + +```sh +pip install cz_legacy +``` + +## Usage + +See the [Conventional Legacy README][cz_legacy] for instructions on configuration. + +[cz_legacy]: https://pypi.org/project/cz_legacy diff --git a/docs/third-party-plugins/cz-path.md b/docs/third-party-plugins/cz-path.md new file mode 100644 index 0000000000..7a2ba59176 --- /dev/null +++ b/docs/third-party-plugins/cz-path.md @@ -0,0 +1,54 @@ +# [cz-path](https://pypi.org/project/cz-path/) + +Provides prefix choices for commit messages based on staged files (Git only). +For example, if the staged files are `component/z/a.ts` and `component/z/b.ts`, +the path prefix option will be `component/z` and commit message might look like: +`component/z/: description of changes`. If only one file is staged, the extension +is removed in the prefix. + +This plugins provides a commitizen convention for commit messages. + +## Installation + +```sh +pip install cz-path +``` + +## Usage + +Add `cz-path` to your configuration file. + +Example for `.cz.json`: + +```json +{ + "commitizen": { + "name": "cz_path", + "remove_path_prefixes": ["src", "module_name"] + } +} +``` + +The default value for `remove_path_prefixes` is `["src"]`. Adding `/` to the +prefixes is not required. + +## Example session + +```plain + $ git add .vscode/ + $ cz -n cz_path c +? Prefix: (Use arrow keys) + » .vscode + .vscode/ + project + (empty) +? Prefix: .vscode +? Commit title: adjust settings + +.vscode: adjust settings + +[main 0000000] .vscode: adjust settings + 2 files changed, 1 insertion(+), 11 deletions(-) + +Commit successful! +``` diff --git a/docs/third-party-plugins/github-jira-conventional.md b/docs/third-party-plugins/github-jira-conventional.md new file mode 100644 index 0000000000..6032b8e7ff --- /dev/null +++ b/docs/third-party-plugins/github-jira-conventional.md @@ -0,0 +1,17 @@ +# [GitHub JIRA Conventional](https://pypi.org/project/cz-github-jira-conventional/) + +This plugin extends the Commitizen tools by: + +- requiring a JIRA issue ID in the commit message +- creating links to GitHub commits in `CHANGELOG.md` +- creating links to JIRA issues in `CHANGELOG.md` + +<!-- TODO: What features does the plugin provide? --> + +## Installation + +```sh +pip install cz-github-jira-conventional +``` + +For installation instructions (configuration and pre-commit), please visit the [GitHub repository](https://github.com/apheris/cz-github-jira-conventional). diff --git a/docs/tutorials/auto_check.md b/docs/tutorials/auto_check.md index 2fce57f9bd..d143528767 100644 --- a/docs/tutorials/auto_check.md +++ b/docs/tutorials/auto_check.md @@ -6,9 +6,9 @@ To automatically check a commit message prior to committing, you can use a [git ## How to -There are two common methods for installing the hook: +There are two common methods for installing the hooks: -### Method 1: Add git hook through [pre-commit](https://pre-commit.com/) +### Method 1: Add a git hook through [pre-commit](https://pre-commit.com/) - Step 1: Install [pre-commit](https://pre-commit.com/) @@ -16,7 +16,7 @@ There are two common methods for installing the hook: python -m pip install pre-commit ``` -- Step 2: Create `.pre-commit-config.yaml` at your root directory with the following content +- Step 2: Create `.pre-commit-config.yaml` in your root directory with the following content ```yaml --- @@ -28,19 +28,19 @@ repos: stages: [commit-msg] ``` -- Step 3: Install the configuration into git hook through `pre-commit` +- Step 3: Install the configuration into the git hook through `pre-commit` ```bash pre-commit install --hook-type commit-msg ``` -### Method 2: Manually add git hook +### Method 2: Manually add a git hook -The command might be included inside of a Git hook (inside of `.git/hooks/` at the root of the project). +The command might be included inside a Git hook (inside `.git/hooks/` at the root of the project). The selected hook might be the file called commit-msg. -This example shows how to use the check command inside of commit-msg. +This example shows how to use the check command inside commit-msg. At the root of the project: @@ -62,7 +62,7 @@ Where `$1` is the name of the temporary file that contains the current commit me The `--commit-msg-file` flag is required, not optional. -Each time you create a commit, automatically, this hook will analyze it. -If the commit message is invalid, it'll be rejected. +Each time you create a commit, this hook will automatically analyze it. +If the commit message is invalid, it will be rejected. The commit should follow the given committing rules; otherwise, it won't be accepted. diff --git a/docs/tutorials/auto_prepare_commit_message.md b/docs/tutorials/auto_prepare_commit_message.md index 7e8295b7c8..84ac62b689 100644 --- a/docs/tutorials/auto_prepare_commit_message.md +++ b/docs/tutorials/auto_prepare_commit_message.md @@ -2,7 +2,7 @@ ## About -It can be desirable to use commitizen for all types of commits (i.e. regular, merge, +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`. @@ -18,10 +18,10 @@ To automatically perform arbitrary cleanup steps after a successful commit you c > 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 +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. +Commitizen and use the generated commit message for the commit. ## Installation diff --git a/docs/tutorials/dev_releases.md b/docs/tutorials/dev_releases.md index 8142334754..e2b29fb191 100644 --- a/docs/tutorials/dev_releases.md +++ b/docs/tutorials/dev_releases.md @@ -5,9 +5,7 @@ To make use of a `.dev` suffix, as per [PEP440](https://peps.python.org/pep-0440/#developmental-releases). -If more than one active branch attempts to create a tag, relative to the main -branch, there is the possibility that each will attempt to create the _same_ -tag, resulting in a collision. +If multiple active branches attempt to create a tag relative to the main branch, there is a possibility that they will attempt to create the _same_ tag, resulting in a collision. Developmental releases aim to avoid this by including a `.dev` segment which includes a non-negative integer unique to that workflow: @@ -19,9 +17,7 @@ X.Y.devN !!! note As noted in [PEP440](https://peps.python.org/pep-0440/#developmental-releases), - although developmental releases are useful in avoiding the situation - described above, depending on the value passed as the developmental - release, they can be _"difficult to parse for human readers"_. + while developmental releases help avoid the situation described above, they can be _"difficult to parse for human readers"_ depending on the value passed as the developmental release. ## How to @@ -64,8 +60,7 @@ Equally, as the developmental release needs only a non-negative integer, it is possible to use the Unix time (i.e. the number of seconds since 1st January 1970 UTC). -This would create the possibility of a collision if two builds occur at -precisely the same second but this may be sufficient for many cases: +This approach could potentially create a collision if two builds occur at precisely the same second, but it may be sufficient for many use cases: ```sh --devrelease $(date +%s) diff --git a/docs/tutorials/github_actions.md b/docs/tutorials/github_actions.md index 7a98abe2be..2cb58cfee6 100644 --- a/docs/tutorials/github_actions.md +++ b/docs/tutorials/github_actions.md @@ -1,4 +1,4 @@ -## Create a new release with Github Actions +## Create a new release with GitHub Actions ### Automatic bumping of version @@ -41,17 +41,17 @@ jobs: Push to master and that's it. -### Creating a github release +### Creating a GitHub release You can modify the previous action. Add the variable `changelog_increment_filename` in the `commitizen-action`, specifying where to output the content of the changelog for the newly created version. -And then add a step using a github action to create the release: `softprops/action-gh-release` +And then add a step using a GitHub action to create the release: `softprops/action-gh-release` -The commitizen action creates an env variable called `REVISION`, containing the -newely created version. +Commitizen action creates an env variable called `REVISION`, containing the +newly created version. ```yaml - name: Create bump and changelog @@ -119,7 +119,7 @@ jobs: ./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) +Notice that we are using poetry, and we are calling a bash script in `./scripts/publish`. You should configure the action, and 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 85abb3fe6d..6f6d53a57e 100644 --- a/docs/tutorials/gitlab_ci.md +++ b/docs/tutorials/gitlab_ci.md @@ -12,9 +12,9 @@ _Goal_: Bump a new version every time that a change occurs on the `master` branc 4. For simplification, we store the software version in a file called `VERSION`. You can use any file that you want as `commitizen` supports it. 5. The commit message executed automatically by the `CI` must include `[skip-ci]` in the message; otherwise, the process will generate a loop. You can define the message structure in [commitizen](../commands/bump.md) as well. -### Gitlab Configuration +### GitLab Configuration -To be able to change files and push new changes with `Gitlab CI` runners, we need to have a `ssh` key and configure a git user. +To be able to change files and push new changes with `GitLab CI` runners, we need to have a `ssh` key and configure a git user. First, let's create a `ssh key`. The only requirement is to create it without a passphrase: @@ -38,7 +38,7 @@ The latest step is to create a `deploy key.` To do this, we should create it und If you have more projects under the same organization, you can reuse the deploy key created before, but you will have to repeat the step where we have created the environment variables (ssh key, email, and username). -tip: If the CI raise some errors, try to unprotected the private key. +Tip: If the CI raise some errors, try to unprotect the private key. ### Defining GitLab CI Pipeline @@ -105,9 +105,9 @@ auto-bump: - variables ``` -So, every time that a developer push to any branch, the `test` job is executed. If the branch is `master` and the test jobs success, the `auto-bump` takes place. -To be able to push using the Gitlab runner, we have to set the ssh key, configure git, and finally execute the auto bump. +So, every time that a developer pushes to any branch, the `test` job is executed. If the branch is `master` and the test jobs succeed, the `auto-bump` takes place. +To be able to push using the GitLab runner, we have to set the SSH key, configure git, and finally execute the auto bump. -After merging the new changed into master, we have the final result: +After merging the new changes into master, we have the final result: ![gitlab final ci result](../images/gitlab_ci/gitlab_final_ci_result.png) diff --git a/docs/tutorials/jenkins_pipeline.md b/docs/tutorials/jenkins_pipeline.md index fb87820c4c..2b9ad173d3 100644 --- a/docs/tutorials/jenkins_pipeline.md +++ b/docs/tutorials/jenkins_pipeline.md @@ -47,7 +47,7 @@ def useCz(String authorName = 'Jenkins CI Server', String authorEmail = 'your-je ``` !!! warning - Using jenkins pipeline with any git plugin may require many different configurations, + Using jenkins pipeline with any git plugin may require many configurations, you'll have to tinker with it until your pipelines properly detects git events. Check your webhook in your git repository and check the "behaviors" and "build strategies" in your pipeline settings. diff --git a/docs/tutorials/monorepo_guidance.md b/docs/tutorials/monorepo_guidance.md index 792c8c224f..f863a9e531 100644 --- a/docs/tutorials/monorepo_guidance.md +++ b/docs/tutorials/monorepo_guidance.md @@ -1,81 +1,87 @@ -# Configuring commitizen in a monorepo - -This tutorial assumes the monorepo layout is designed with multiple components that can be released independently of each -other, it also assumes that conventional commits with scopes are in use. Some suggested layouts: - -```shell-session -. -├── library-b -│   └── .cz.toml -└── library-z - └── .cz.toml -``` - -```shell-session -src -├── library-b -│   └── .cz.toml -└── library-z - └── .cz.toml -``` - -Sample `.cz.toml` for each component: - -```toml -# library-b/.cz.toml -[tool.commitizen] -name = "cz_customize" -version = "0.0.0" -tag_format = "${version}-library-b" # the component name can be a prefix or suffix with or without a separator -ignored_tag_formats = ["${version}-library-*"] # Avoid noise from other tags -update_changelog_on_bump = true -``` - -```toml -# library-z/.cz.toml -[tool.commitizen] -name = "cz_customize" -version = "0.0.0" -tag_format = "${version}-library-z" -ignored_tag_formats = ["${version}-library-*"] # Avoid noise from other tags -update_changelog_on_bump = true -``` - -And finally, to bump each of these: - -```sh -cz --config library-b/.cz.toml bump --yes -cz --config library-z/.cz.toml bump --yes -``` +# Configuring Commitizen in a monorepo + +This tutorial assumes that your monorepo is structured with multiple components that can be released independently of each other. +It also assumes that you are using conventional commits with scopes. + +Here is a step-by-step example using two libraries, `library-b` and `library-z`: + +1. **Organize your monorepo** + + For example, you might have one of these layouts: + + ```shell-session + . + ├── library-b + │   └── .cz.toml + └── library-z + └── .cz.toml + ``` + + ```shell-session + src + ├── library-b + │   └── .cz.toml + └── library-z + └── .cz.toml + ``` + +2. **Add a Commitizen configuration for each component** + + ```toml + # library-b/.cz.toml + [tool.commitizen] + name = "cz_customize" + version = "0.0.0" + tag_format = "${version}-library-b" # the component name can be a prefix or suffix with or without a separator + ignored_tag_formats = ["${version}-library-*"] # Avoid noise from other tags + update_changelog_on_bump = true + ``` + + ```toml + # library-z/.cz.toml + [tool.commitizen] + name = "cz_customize" + version = "0.0.0" + tag_format = "${version}-library-z" + ignored_tag_formats = ["${version}-library-*"] # Avoid noise from other tags + update_changelog_on_bump = true + ``` + +3. **Bump each component independently** + + ```sh + cz --config library-b/.cz.toml bump --yes + cz --config library-z/.cz.toml bump --yes + ``` ## Changelog per component -In order to filter the correct commits for each component, you'll have to come up with a strategy. +To filter the correct commits for each component, you'll need to define a strategy. For example: -- Trigger the pipeline based on the changed path, which can have some downsides, as you'll rely on the developer not including files from other files - - [github actions](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore) uses `path` +- Trigger the pipeline based on the changed path. This can have some downsides, as you'll rely on the developer not including files from unrelated components. + - [GitHub Actions](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore) uses `path` - [Jenkins](https://www.jenkins.io/doc/book/pipeline/syntax/#built-in-conditions) uses `changeset` - - [Gitlab](https://docs.gitlab.com/ee/ci/yaml/#ruleschanges) uses `rules:changes` -- Filter certain pattern of the commit message (recommended) + - [GitLab](https://docs.gitlab.com/ee/ci/yaml/#ruleschanges) uses `rules:changes` +- Filter commits by a specific pattern in the commit message (recommended) ### Example with scope in conventional commits -For this example, to include the message in the changelog, we will require commits to use a specific scope. -This way, only relevant commits will be included in the appropriate change log for a given component, and any other commit will be ignored. +In this example, we want `library-b`'s changelog to only include commits that use the `library-b` scope. +To achieve this, we configure Commitizen to match only commit messages with that scope. -Example config and commit for `library-b`: +Here is an example configuration for `library-b`: ```toml [tool.commitizen.customize] -changelog_pattern = "^(feat|fix)\\(library-b\\)(!)?:" #the pattern on types can be a wild card or any types you wish to include +changelog_pattern = "^(feat|fix)\\(library-b\\)(!)?:" # the type pattern can be a wildcard or any types you wish to include ``` -A commit message looking like this, would be included: +With this configuration, a commit message like the following would be included in `library-b`'s changelog: -``` +```text fix(library-b): Some awesome message ``` diff --git a/docs/tutorials/tag_format.md b/docs/tutorials/tag_format.md index 59c42bea13..2d439e38ca 100644 --- a/docs/tutorials/tag_format.md +++ b/docs/tutorials/tag_format.md @@ -10,7 +10,7 @@ tag_format: $version version_scheme: pep440 ``` -As this is the default value so you don't have to specify it. +As this is the default value, you don't have to specify it. This setting means that: @@ -42,8 +42,7 @@ As a result, the tag generated on bump will have this format: `v1.0.0` and the v !!! note Both `$version` and `${version}` syntaxes are strictly equivalent. You can use the one you prefer. -See [the `version_scheme` section in `bump` command documentation](../commands/bump.md#version_scheme) for more details on version schemes and how to define your own. -See [`tag_format`](../config.md#tag_format) and [`version_scheme`](../config.md#version_scheme) settings in [Configuration reference](../config.md) for more details on these settings. +See [`tag_format`](../commands/bump.md#-tag-format) and [`version_scheme`](../commands/bump.md#-version-scheme) settings for more details. ## Changing convention @@ -52,7 +51,7 @@ You will obviously want to keep all those features working as expected. Commitizen can deal with it as long as you provide the legacy tag format in the configuration. -Using the previous example, let say you want to move from `v${version}` to `component-${version}`. +Using the previous example, let's say you want to move from `v${version}` to `component-${version}`. Then `component-${version}` will be the new tag format and `v${version}` the legacy one. ```yaml @@ -62,16 +61,16 @@ legacy_tag_formats: - v${version} ``` -This way, you won't loose your version history, you'll still be able to generate you changelog properly -and on the next version bump, your last version in the form `v${version}` will be properly recognizef if you use the `scm` version provider. +This way, you won't lose your version history, you'll still be able to generate your changelog properly, +and on the next version bump, your last version in the form `v${version}` will be properly recognized if you use the `scm` version provider. Your new tag will be in the form `component-${version}`. ## Known tags to ignore -Now let's say you have some known tags you want to ignore, either because they are not versions, either because they are not versions of the component you are dealing with. -As a consequence, you don't want them to trigger a warning because Commitizen detected an unknown tag format: +Now let's say you have some known tags you want to ignore, either because they are not versions, or because they are not versions of the component you are dealing with. +As a consequence, you don't want them to trigger a warning because Commitizen detected an unknown tag format. -Then you can tell Commitizen about it using the [`ignored_tag_formats`](../config.md#ignored_tag_formats) setting: +Then you can tell Commitizen about it using the [`ignored_tag_formats`](../config/bump.md#ignored_tag_formats) setting: ```yaml [tool.commitizen] @@ -93,7 +92,7 @@ This will ignore: It will match any string from any length. This allows to exclude by prefix, whether it is followed by a version or not. !!! tip - If you don't want to be warned when Commitizen detect an unknown tag, you can by setting: + If you don't want to be warned when Commitizen detects an unknown tag, you can do so by setting: ``` [tool.commitizen] ignored_tag_formats = ["*"] diff --git a/docs/tutorials/writing_commits.md b/docs/tutorials/writing_commits.md index 9ba151cc37..a56d6494d4 100644 --- a/docs/tutorials/writing_commits.md +++ b/docs/tutorials/writing_commits.md @@ -1,21 +1,25 @@ For this project to work well in your pipeline, a commit convention must be followed. -By default commitizen uses the known [conventional commits][conventional_commits], but -you can create your own following the docs information over at +By default, Commitizen uses the known [conventional commits][conventional_commits], but +you can create your own following the documentation information over at [customization][customization]. ## Conventional commits If you are using [conventional commits][conventional_commits], the most important thing to know is that you must begin your commits with at least one of these tags: -`fix`, `feat`. And if you introduce a breaking change, then, you must +`fix`, `feat`. And if you introduce a breaking change, then you must add to your commit body the following `BREAKING CHANGE`. -Using these 3 keywords will allow the proper identification of the semantic version. +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: +Now to the important part: when writing commits, it's important to think about: - Your future self - Your colleagues @@ -23,16 +27,16 @@ Now to the important part, when writing commits, it's important to think about: You may think this is trivial, but it's not. It's important for the reader to understand what happened. -Emojis may be added as well (e.g. see [cz-emoji][cz_emoji]), which requires the `utf-8`, or equivalent, character encoding to support unicode characters. By default, `commitizen` uses the `utf-8` character encoding, but a different encoding may be set through the `encoding` [configuration option][configuration]. +Emojis may be added as well (e.g., see [cz-emoji][cz_emoji]), which requires the `utf-8`, or equivalent, character encoding to support unicode characters. By default, `commitizen` uses the `utf-8` character encoding, but a different encoding may be set through the `encoding` [configuration option][configuration]. ### Recommendations - **Keep the message short**: Makes the list of commits more readable (~50 chars). - **Talk imperative**: Follow this rule: `If applied, this commit will <commit message>` -- **Think about the CHANGELOG**: Your commits will probably end up in the changelog +- **Think about the CHANGELOG**: Your commits will probably end up in the changelog, so try writing for it, but also keep in mind that you can skip sending commits to the CHANGELOG by using different keywords (like `build`). -- **Use a commit per new feature**: if you introduce multiple things related to the same +- **Use a commit per new feature**: If you introduce multiple things related to the same commit, squash them. This is useful for auto-generating CHANGELOG. | Do's | Don'ts | @@ -40,7 +44,8 @@ Emojis may be added as well (e.g. see [cz-emoji][cz_emoji]), which requires the | `fix(commands): bump error when no user provided` | `fix: stuff` | | `feat: add new commit command` | `feat: commit command introduced` | -[customization]: ../customization.md +[customization]: ../customization/config_file.md [conventional_commits]: https://www.conventionalcommits.org [cz_emoji]: https://commitizen-tools.github.io/commitizen/third-party-commitizen/#cz-emoji -[configuration]: ../config.md#encoding +[configuration]: ../config/commit.md#encoding +[breaking-change-config]: ../config/commit.md#breaking_change_exclamation_in_title diff --git a/hooks/post-commit.py b/hooks/post-commit.py index c7dea825bd..d242b6b354 100755 --- a/hooks/post-commit.py +++ b/hooks/post-commit.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -from pathlib import Path try: from commitizen.cz.utils import get_backup_file_path @@ -9,11 +8,11 @@ def post_commit() -> None: - backup_file = Path(get_backup_file_path()) + backup_file_path = get_backup_file_path() # remove backup file if it exists - if backup_file.is_file(): - backup_file.unlink() + if backup_file_path.is_file(): + backup_file_path.unlink() if __name__ == "__main__": diff --git a/hooks/prepare-commit-msg.py b/hooks/prepare-commit-msg.py index e666fa673b..017ecc28ea 100755 --- a/hooks/prepare-commit-msg.py +++ b/hooks/prepare-commit-msg.py @@ -3,7 +3,6 @@ import subprocess import sys from pathlib import Path -from subprocess import CalledProcessError try: from commitizen.cz.utils import get_backup_file_path @@ -24,33 +23,34 @@ def prepare_commit_msg(commit_msg_file: str) -> int: ], capture_output=True, ).returncode - if exit_code != 0: - backup_file = Path(get_backup_file_path()) - 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 exit_code == 0: + return 0 + + backup_file = Path(get_backup_file_path()) + 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 + exit_code = subprocess.run( + [ + "cz", + "commit", + "--dry-run", + "--write-message-to-file", + commit_msg_file, + ], + stdin=sys.stdin, + stdout=sys.stdout, + ).returncode + if exit_code: + return exit_code + + # write message to backup file + shutil.copyfile(commit_msg_file, backup_file) return 0 @@ -58,4 +58,5 @@ def prepare_commit_msg(commit_msg_file: str) -> int: # 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])) + exit_code = prepare_commit_msg(sys.argv[1]) + exit(exit_code) diff --git a/mkdocs.yml b/mkdocs.yml index 6a642161d2..9666a203db 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,10 +1,10 @@ site_name: Commitizen +site_url: https://commitizen-tools.github.io/commitizen/ site_description: commit rules, semantic version, conventional commits theme: - name: "material" + name: material palette: - - primary: 'deep purple' # Palette toggle for automatic mode - media: "(prefers-color-scheme)" toggle: @@ -14,6 +14,7 @@ theme: # Palette toggle for light mode - media: "(prefers-color-scheme: light)" scheme: default + primary: deep purple toggle: icon: material/brightness-7 name: Switch to dark mode @@ -21,6 +22,7 @@ theme: # Palette toggle for dark mode - media: "(prefers-color-scheme: dark)" scheme: slate + primary: deep purple toggle: icon: material/brightness-4 name: Switch to system preference @@ -31,34 +33,56 @@ edit_uri: "" nav: - Introduction: "README.md" - - Getting Started: "getting_started.md" - Commands: - - init: "commands/init.md" - - commit: "commands/commit.md" - - bump: "commands/bump.md" - - check: "commands/check.md" - - changelog: "commands/changelog.md" - - example: "commands/example.md" - - info: "commands/info.md" - - ls: "commands/ls.md" - - schema: "commands/schema.md" - - version: "commands/version.md" - - Configuration: "config.md" - - Customization: "customization.md" + - init: "commands/init.md" + - commit: "commands/commit.md" + - bump: "commands/bump.md" + - check: "commands/check.md" + - changelog: "commands/changelog.md" + - example: "commands/example.md" + - info: "commands/info.md" + - ls: "commands/ls.md" + - schema: "commands/schema.md" + - version: "commands/version.md" + - Configuration: + - Configuration File: "config/configuration_file.md" + - Version Provider: "config/version_provider.md" + - bump: "config/bump.md" + - commit: "config/commit.md" + - check: "config/check.md" + - changelog: "config/changelog.md" + - Misc Options: "config/option.md" + - Advanced Customization: + - Configuration File: "customization/config_file.md" + - Customized Python Class: "customization/python_class.md" + - Changelog Template: "customization/changelog_template.md" - Tutorials: - - Writing commits: "tutorials/writing_commits.md" - - Managing tags formats: "tutorials/tag_format.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" - - Developmental releases: "tutorials/dev_releases.md" - - Monorepo support: "tutorials/monorepo_guidance.md" + - Writing commits: "tutorials/writing_commits.md" + - Managing tags formats: "tutorials/tag_format.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" + - Developmental releases: "tutorials/dev_releases.md" + - Monorepo support: "tutorials/monorepo_guidance.md" - FAQ: "faq.md" + - Features we won't add: "features_wont_add.md" - Exit Codes: "exit_codes.md" - - Third-Party Commitizen Templates: "third-party-commitizen.md" + - Third-Party Commitizen Plugins: + - About: "third-party-plugins/about.md" + # Please sort the plugins alphabetically + - "third-party-plugins/commitizen-deno-provider.md" + - "third-party-plugins/commitizen-emoji.md" + - "third-party-plugins/conventional-jira.md" + - "third-party-plugins/cz-ai.md" + - "third-party-plugins/cz-conventional-gitmoji.md" + - "third-party-plugins/cz-emoji.md" + - "third-party-plugins/cz-legacy.md" + - "third-party-plugins/cz-path.md" + - "third-party-plugins/github-jira-conventional.md" - Contributing: "contributing.md" + - Contributing TL;DR: "contributing_tldr.md" - Resources: "external_links.md" markdown_extensions: diff --git a/poetry.lock b/poetry.lock index cccd7a532a..e7600db5d3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,15 +1,15 @@ -# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "argcomplete" -version = "3.6.2" +version = "3.6.3" description = "Bash tab completion for argparse" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "argcomplete-3.6.2-py3-none-any.whl", hash = "sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591"}, - {file = "argcomplete-3.6.2.tar.gz", hash = "sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf"}, + {file = "argcomplete-3.6.3-py3-none-any.whl", hash = "sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce"}, + {file = "argcomplete-3.6.3.tar.gz", hash = "sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c"}, ] [package.extras] @@ -17,60 +17,77 @@ test = ["coverage", "mypy", "pexpect", "ruff", "wheel"] [[package]] name = "asttokens" -version = "2.4.1" +version = "3.0.1" description = "Annotate AST trees with source code positions" optional = false -python-versions = "*" +python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, + {file = "asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a"}, + {file = "asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7"}, ] -[package.dependencies] -six = ">=1.12.0" - [package.extras] -astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] -test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] +astroid = ["astroid (>=2,<5)"] +test = ["astroid (>=2,<5)", "pytest (<9.0)", "pytest-cov", "pytest-xdist"] [[package]] name = "babel" -version = "2.16.0" +version = "2.17.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" groups = ["documentation"] files = [ - {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, - {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, + {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, + {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, +] + +[package.extras] +dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] + +[[package]] +name = "backrefs" +version = "6.1" +description = "A wrapper around re and regex that adds additional back references." +optional = false +python-versions = ">=3.9" +groups = ["documentation"] +files = [ + {file = "backrefs-6.1-py310-none-any.whl", hash = "sha256:2a2ccb96302337ce61ee4717ceacfbf26ba4efb1d55af86564b8bbaeda39cac1"}, + {file = "backrefs-6.1-py311-none-any.whl", hash = "sha256:e82bba3875ee4430f4de4b6db19429a27275d95a5f3773c57e9e18abc23fd2b7"}, + {file = "backrefs-6.1-py312-none-any.whl", hash = "sha256:c64698c8d2269343d88947c0735cb4b78745bd3ba590e10313fbf3f78c34da5a"}, + {file = "backrefs-6.1-py313-none-any.whl", hash = "sha256:4c9d3dc1e2e558965202c012304f33d4e0e477e1c103663fd2c3cc9bb18b0d05"}, + {file = "backrefs-6.1-py314-none-any.whl", hash = "sha256:13eafbc9ccd5222e9c1f0bec563e6d2a6d21514962f11e7fc79872fd56cbc853"}, + {file = "backrefs-6.1-py39-none-any.whl", hash = "sha256:a9e99b8a4867852cad177a6430e31b0f6e495d65f8c6c134b68c14c3c95bf4b0"}, + {file = "backrefs-6.1.tar.gz", hash = "sha256:3bba1749aafe1db9b915f00e0dd166cba613b6f788ffd63060ac3485dc9be231"}, ] [package.extras] -dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] +extras = ["regex"] [[package]] name = "cachetools" -version = "5.5.1" +version = "6.2.2" description = "Extensible memoizing collections and decorators" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb"}, - {file = "cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95"}, + {file = "cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace"}, + {file = "cachetools-6.2.2.tar.gz", hash = "sha256:8e6d266b25e539df852251cfd6f990b4bc3a141db73b939058d809ebd2590fc6"}, ] [[package]] name = "certifi" -version = "2024.8.30" +version = "2025.11.12" description = "Python package for providing Mozilla's CA Bundle." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" groups = ["documentation"] files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, + {file = "certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b"}, + {file = "certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316"}, ] [[package]] @@ -99,116 +116,137 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.4.1" +version = "3.4.4" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["main", "documentation"] files = [ - {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, - {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, - {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win32.whl", hash = "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50"}, + {file = "charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f"}, + {file = "charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a"}, ] [[package]] name = "click" -version = "8.1.7" +version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" groups = ["documentation"] files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, ] [package.dependencies] @@ -228,162 +266,207 @@ files = [ [[package]] name = "coverage" -version = "7.6.8" +version = "7.10.7" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "coverage-7.6.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b39e6011cd06822eb964d038d5dff5da5d98652b81f5ecd439277b32361a3a50"}, - {file = "coverage-7.6.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63c19702db10ad79151a059d2d6336fe0c470f2e18d0d4d1a57f7f9713875dcf"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3985b9be361d8fb6b2d1adc9924d01dec575a1d7453a14cccd73225cb79243ee"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:644ec81edec0f4ad17d51c838a7d01e42811054543b76d4ba2c5d6af741ce2a6"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f188a2402f8359cf0c4b1fe89eea40dc13b52e7b4fd4812450da9fcd210181d"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19122296822deafce89a0c5e8685704c067ae65d45e79718c92df7b3ec3d331"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13618bed0c38acc418896005732e565b317aa9e98d855a0e9f211a7ffc2d6638"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:193e3bffca48ad74b8c764fb4492dd875038a2f9925530cb094db92bb5e47bed"}, - {file = "coverage-7.6.8-cp310-cp310-win32.whl", hash = "sha256:3988665ee376abce49613701336544041f2117de7b7fbfe91b93d8ff8b151c8e"}, - {file = "coverage-7.6.8-cp310-cp310-win_amd64.whl", hash = "sha256:f56f49b2553d7dd85fd86e029515a221e5c1f8cb3d9c38b470bc38bde7b8445a"}, - {file = "coverage-7.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86cffe9c6dfcfe22e28027069725c7f57f4b868a3f86e81d1c62462764dc46d4"}, - {file = "coverage-7.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d82ab6816c3277dc962cfcdc85b1efa0e5f50fb2c449432deaf2398a2928ab94"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13690e923a3932e4fad4c0ebfb9cb5988e03d9dcb4c5150b5fcbf58fd8bddfc4"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be32da0c3827ac9132bb488d331cb32e8d9638dd41a0557c5569d57cf22c9c1"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e6c85bbdc809383b509d732b06419fb4544dca29ebe18480379633623baafb"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:768939f7c4353c0fac2f7c37897e10b1414b571fd85dd9fc49e6a87e37a2e0d8"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e44961e36cb13c495806d4cac67640ac2866cb99044e210895b506c26ee63d3a"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ea8bb1ab9558374c0ab591783808511d135a833c3ca64a18ec927f20c4030f0"}, - {file = "coverage-7.6.8-cp311-cp311-win32.whl", hash = "sha256:629a1ba2115dce8bf75a5cce9f2486ae483cb89c0145795603d6554bdc83e801"}, - {file = "coverage-7.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:fb9fc32399dca861584d96eccd6c980b69bbcd7c228d06fb74fe53e007aa8ef9"}, - {file = "coverage-7.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e683e6ecc587643f8cde8f5da6768e9d165cd31edf39ee90ed7034f9ca0eefee"}, - {file = "coverage-7.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1defe91d41ce1bd44b40fabf071e6a01a5aa14de4a31b986aa9dfd1b3e3e414a"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7ad66e8e50225ebf4236368cc43c37f59d5e6728f15f6e258c8639fa0dd8e6d"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fe47da3e4fda5f1abb5709c156eca207eacf8007304ce3019eb001e7a7204cb"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:202a2d645c5a46b84992f55b0a3affe4f0ba6b4c611abec32ee88358db4bb649"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4674f0daa1823c295845b6a740d98a840d7a1c11df00d1fd62614545c1583787"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:74610105ebd6f33d7c10f8907afed696e79c59e3043c5f20eaa3a46fddf33b4c"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37cda8712145917105e07aab96388ae76e787270ec04bcb9d5cc786d7cbb8443"}, - {file = "coverage-7.6.8-cp312-cp312-win32.whl", hash = "sha256:9e89d5c8509fbd6c03d0dd1972925b22f50db0792ce06324ba069f10787429ad"}, - {file = "coverage-7.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:379c111d3558272a2cae3d8e57e6b6e6f4fe652905692d54bad5ea0ca37c5ad4"}, - {file = "coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb"}, - {file = "coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b"}, - {file = "coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146"}, - {file = "coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28"}, - {file = "coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d"}, - {file = "coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b"}, - {file = "coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71"}, - {file = "coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc"}, - {file = "coverage-7.6.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ac47fa29d8d41059ea3df65bd3ade92f97ee4910ed638e87075b8e8ce69599e"}, - {file = "coverage-7.6.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:24eda3a24a38157eee639ca9afe45eefa8d2420d49468819ac5f88b10de84f4c"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c81ed2820b9023a9a90717020315e63b17b18c274a332e3b6437d7ff70abe0"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd55f8fc8fa494958772a2a7302b0354ab16e0b9272b3c3d83cdb5bec5bd1779"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f39e2f3530ed1626c66e7493be7a8423b023ca852aacdc91fb30162c350d2a92"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:716a78a342679cd1177bc8c2fe957e0ab91405bd43a17094324845200b2fddf4"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:177f01eeaa3aee4a5ffb0d1439c5952b53d5010f86e9d2667963e632e30082cc"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:912e95017ff51dc3d7b6e2be158dedc889d9a5cc3382445589ce554f1a34c0ea"}, - {file = "coverage-7.6.8-cp39-cp39-win32.whl", hash = "sha256:4db3ed6a907b555e57cc2e6f14dc3a4c2458cdad8919e40b5357ab9b6db6c43e"}, - {file = "coverage-7.6.8-cp39-cp39-win_amd64.whl", hash = "sha256:428ac484592f780e8cd7b6b14eb568f7c85460c92e2a37cb0c0e5186e1a0d076"}, - {file = "coverage-7.6.8-pp39.pp310-none-any.whl", hash = "sha256:5c52a036535d12590c32c49209e79cabaad9f9ad8aa4cbd875b68c4d67a9cbce"}, - {file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"}, + {file = "coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a"}, + {file = "coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5"}, + {file = "coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17"}, + {file = "coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b"}, + {file = "coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87"}, + {file = "coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e"}, + {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e"}, + {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df"}, + {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0"}, + {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13"}, + {file = "coverage-7.10.7-cp310-cp310-win32.whl", hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b"}, + {file = "coverage-7.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807"}, + {file = "coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59"}, + {file = "coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a"}, + {file = "coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699"}, + {file = "coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d"}, + {file = "coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e"}, + {file = "coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23"}, + {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab"}, + {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82"}, + {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2"}, + {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61"}, + {file = "coverage-7.10.7-cp311-cp311-win32.whl", hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14"}, + {file = "coverage-7.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2"}, + {file = "coverage-7.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a"}, + {file = "coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417"}, + {file = "coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973"}, + {file = "coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c"}, + {file = "coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7"}, + {file = "coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6"}, + {file = "coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59"}, + {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b"}, + {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a"}, + {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb"}, + {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1"}, + {file = "coverage-7.10.7-cp312-cp312-win32.whl", hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256"}, + {file = "coverage-7.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba"}, + {file = "coverage-7.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf"}, + {file = "coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d"}, + {file = "coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b"}, + {file = "coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e"}, + {file = "coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b"}, + {file = "coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49"}, + {file = "coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911"}, + {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0"}, + {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f"}, + {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c"}, + {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f"}, + {file = "coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698"}, + {file = "coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843"}, + {file = "coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546"}, + {file = "coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c"}, + {file = "coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15"}, + {file = "coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4"}, + {file = "coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0"}, + {file = "coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0"}, + {file = "coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65"}, + {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541"}, + {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6"}, + {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999"}, + {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2"}, + {file = "coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a"}, + {file = "coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb"}, + {file = "coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb"}, + {file = "coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520"}, + {file = "coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32"}, + {file = "coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f"}, + {file = "coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a"}, + {file = "coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360"}, + {file = "coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69"}, + {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14"}, + {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe"}, + {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e"}, + {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd"}, + {file = "coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2"}, + {file = "coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681"}, + {file = "coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880"}, + {file = "coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63"}, + {file = "coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2"}, + {file = "coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d"}, + {file = "coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0"}, + {file = "coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699"}, + {file = "coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9"}, + {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f"}, + {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1"}, + {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0"}, + {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399"}, + {file = "coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235"}, + {file = "coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d"}, + {file = "coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a"}, + {file = "coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3"}, + {file = "coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c"}, + {file = "coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396"}, + {file = "coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40"}, + {file = "coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594"}, + {file = "coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a"}, + {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b"}, + {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3"}, + {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0"}, + {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f"}, + {file = "coverage-7.10.7-cp39-cp39-win32.whl", hash = "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431"}, + {file = "coverage-7.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07"}, + {file = "coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260"}, + {file = "coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] -toml = ["tomli"] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "decli" -version = "0.6.2" +version = "0.6.3" description = "Minimal, easy-to-use, declarative cli tool" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "decli-0.6.2-py3-none-any.whl", hash = "sha256:2fc84106ce9a8f523ed501ca543bdb7e416c064917c12a59ebdc7f311a97b7ed"}, - {file = "decli-0.6.2.tar.gz", hash = "sha256:36f71eb55fd0093895efb4f416ec32b7f6e00147dda448e3365cf73ceab42d6f"}, + {file = "decli-0.6.3-py3-none-any.whl", hash = "sha256:5152347c7bb8e3114ad65db719e5709b28d7f7f45bdb709f70167925e55640f3"}, + {file = "decli-0.6.3.tar.gz", hash = "sha256:87f9d39361adf7f16b9ca6e3b614badf7519da13092f2db3c80ca223c53c7656"}, ] [[package]] name = "decorator" -version = "5.1.1" +version = "5.2.1" description = "Decorators for Humans" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, - {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, + {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"}, + {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"}, ] [[package]] name = "deprecated" -version = "1.2.18" +version = "1.3.1" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -groups = ["test"] +groups = ["main"] files = [ - {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, - {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, + {file = "deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f"}, + {file = "deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223"}, ] [package.dependencies] -wrapt = ">=1.10,<2" +wrapt = ">=1.10,<3" [package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"] [[package]] name = "distlib" -version = "0.3.9" +version = "0.4.0" description = "Distribution utilities" optional = false python-versions = "*" groups = ["dev", "linters"] files = [ - {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, - {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, + {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"}, + {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"}, ] [[package]] name = "exceptiongroup" -version = "1.2.2" +version = "1.3.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["dev", "test"] markers = "python_version < \"3.11\"" files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + [package.extras] test = ["pytest (>=6)"] [[package]] name = "execnet" -version = "2.1.1" +version = "2.1.2" description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" groups = ["test"] files = [ - {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, - {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, ] [package.extras] @@ -391,46 +474,41 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "executing" -version = "2.1.0" +version = "2.2.1" description = "Get the currently executing AST node of a frame, and other information" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"}, - {file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"}, + {file = "executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017"}, + {file = "executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4"}, ] [package.extras] -tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""] [[package]] name = "filelock" -version = "3.16.1" +version = "3.19.1" description = "A platform independent file lock." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev", "linters"] files = [ - {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, - {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, + {file = "filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d"}, + {file = "filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58"}, ] -[package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] -typing = ["typing-extensions (>=4.12.2)"] - [[package]] name = "freezegun" -version = "1.5.1" +version = "1.5.5" description = "Let your Python tests travel through time" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["test"] files = [ - {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, - {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, + {file = "freezegun-1.5.5-py3-none-any.whl", hash = "sha256:cd557f4a75cf074e84bc374249b9dd491eaeacd61376b9eb3c423282211619d2"}, + {file = "freezegun-1.5.5.tar.gz", hash = "sha256:ac7742a6cc6c25a2c35e9292dfd554b897b517d2dec26891a2e8debf205cb94a"}, ] [package.dependencies] @@ -456,14 +534,14 @@ dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "identify" -version = "2.6.3" +version = "2.6.15" description = "File identification library for Python" optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "identify-2.6.3-py2.py3-none-any.whl", hash = "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd"}, - {file = "identify-2.6.3.tar.gz", hash = "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02"}, + {file = "identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757"}, + {file = "identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf"}, ] [package.extras] @@ -471,14 +549,14 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.10" +version = "3.11" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" groups = ["documentation"] files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, ] [package.extras] @@ -491,7 +569,7 @@ description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" groups = ["main", "documentation"] -markers = "python_version < \"3.10\"" +markers = "python_version == \"3.9\"" files = [ {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, @@ -501,24 +579,24 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] name = "iniconfig" -version = "2.0.0" +version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["test"] files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] [[package]] @@ -581,14 +659,14 @@ testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"] [[package]] name = "jinja2" -version = "3.1.5" +version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" groups = ["main", "documentation"] files = [ - {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, - {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, ] [package.dependencies] @@ -597,23 +675,109 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "librt" +version = "0.7.3" +description = "Mypyc runtime library" +optional = false +python-versions = ">=3.9" +groups = ["linters"] +files = [ + {file = "librt-0.7.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2682162855a708e3270eba4b92026b93f8257c3e65278b456c77631faf0f4f7a"}, + {file = "librt-0.7.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:440c788f707c061d237c1e83edf6164ff19f5c0f823a3bf054e88804ebf971ec"}, + {file = "librt-0.7.3-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399938edbd3d78339f797d685142dd8a623dfaded023cf451033c85955e4838a"}, + {file = "librt-0.7.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1975eda520957c6e0eb52d12968dd3609ffb7eef05d4223d097893d6daf1d8a7"}, + {file = "librt-0.7.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9da128d0edf990cf0d2ca011b02cd6f639e79286774bd5b0351245cbb5a6e51"}, + {file = "librt-0.7.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19acfde38cb532a560b98f473adc741c941b7a9bc90f7294bc273d08becb58b"}, + {file = "librt-0.7.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7b4f57f7a0c65821c5441d98c47ff7c01d359b1e12328219709bdd97fdd37f90"}, + {file = "librt-0.7.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:256793988bff98040de23c57cf36e1f4c2f2dc3dcd17537cdac031d3b681db71"}, + {file = "librt-0.7.3-cp310-cp310-win32.whl", hash = "sha256:fcb72249ac4ea81a7baefcbff74df7029c3cb1cf01a711113fa052d563639c9c"}, + {file = "librt-0.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:4887c29cadbdc50640179e3861c276325ff2986791e6044f73136e6e798ff806"}, + {file = "librt-0.7.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:687403cced6a29590e6be6964463835315905221d797bc5c934a98750fe1a9af"}, + {file = "librt-0.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:24d70810f6e2ea853ff79338001533716b373cc0f63e2a0be5bc96129edb5fb5"}, + {file = "librt-0.7.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bf8c7735fbfc0754111f00edda35cf9e98a8d478de6c47b04eaa9cef4300eaa7"}, + {file = "librt-0.7.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32d43610dff472eab939f4d7fbdd240d1667794192690433672ae22d7af8445"}, + {file = "librt-0.7.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:adeaa886d607fb02563c1f625cf2ee58778a2567c0c109378da8f17ec3076ad7"}, + {file = "librt-0.7.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:572a24fc5958c61431da456a0ef1eeea6b4989d81eeb18b8e5f1f3077592200b"}, + {file = "librt-0.7.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6488e69d408b492e08bfb68f20c4a899a354b4386a446ecd490baff8d0862720"}, + {file = "librt-0.7.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ed028fc3d41adda916320712838aec289956c89b4f0a361ceadf83a53b4c047a"}, + {file = "librt-0.7.3-cp311-cp311-win32.whl", hash = "sha256:2cf9d73499486ce39eebbff5f42452518cc1f88d8b7ea4a711ab32962b176ee2"}, + {file = "librt-0.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:35f1609e3484a649bb80431310ddbec81114cd86648f1d9482bc72a3b86ded2e"}, + {file = "librt-0.7.3-cp311-cp311-win_arm64.whl", hash = "sha256:550fdbfbf5bba6a2960b27376ca76d6aaa2bd4b1a06c4255edd8520c306fcfc0"}, + {file = "librt-0.7.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0fa9ac2e49a6bee56e47573a6786cb635e128a7b12a0dc7851090037c0d397a3"}, + {file = "librt-0.7.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e980cf1ed1a2420a6424e2ed884629cdead291686f1048810a817de07b5eb18"}, + {file = "librt-0.7.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e094e445c37c57e9ec612847812c301840239d34ccc5d153a982fa9814478c60"}, + {file = "librt-0.7.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aca73d70c3f553552ba9133d4a09e767dcfeee352d8d8d3eb3f77e38a3beb3ed"}, + {file = "librt-0.7.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c634a0a6db395fdaba0361aa78395597ee72c3aad651b9a307a3a7eaf5efd67e"}, + {file = "librt-0.7.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a59a69deeb458c858b8fea6acf9e2acd5d755d76cd81a655256bc65c20dfff5b"}, + {file = "librt-0.7.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d91e60ac44bbe3a77a67af4a4c13114cbe9f6d540337ce22f2c9eaf7454ca71f"}, + {file = "librt-0.7.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:703456146dc2bf430f7832fd1341adac5c893ec3c1430194fdcefba00012555c"}, + {file = "librt-0.7.3-cp312-cp312-win32.whl", hash = "sha256:b7c1239b64b70be7759554ad1a86288220bbb04d68518b527783c4ad3fb4f80b"}, + {file = "librt-0.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:ef59c938f72bdbc6ab52dc50f81d0637fde0f194b02d636987cea2ab30f8f55a"}, + {file = "librt-0.7.3-cp312-cp312-win_arm64.whl", hash = "sha256:ff21c554304e8226bf80c3a7754be27c6c3549a9fec563a03c06ee8f494da8fc"}, + {file = "librt-0.7.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56f2a47beda8409061bc1c865bef2d4bd9ff9255219402c0817e68ab5ad89aed"}, + {file = "librt-0.7.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:14569ac5dd38cfccf0a14597a88038fb16811a6fede25c67b79c6d50fc2c8fdc"}, + {file = "librt-0.7.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6038ccbd5968325a5d6fd393cf6e00b622a8de545f0994b89dd0f748dcf3e19e"}, + {file = "librt-0.7.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d39079379a9a28e74f4d57dc6357fa310a1977b51ff12239d7271ec7e71d67f5"}, + {file = "librt-0.7.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8837d5a52a2d7aa9f4c3220a8484013aed1d8ad75240d9a75ede63709ef89055"}, + {file = "librt-0.7.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:399bbd7bcc1633c3e356ae274a1deb8781c7bf84d9c7962cc1ae0c6e87837292"}, + {file = "librt-0.7.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8d8cf653e798ee4c4e654062b633db36984a1572f68c3aa25e364a0ddfbbb910"}, + {file = "librt-0.7.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2f03484b54bf4ae80ab2e504a8d99d20d551bfe64a7ec91e218010b467d77093"}, + {file = "librt-0.7.3-cp313-cp313-win32.whl", hash = "sha256:44b3689b040df57f492e02cd4f0bacd1b42c5400e4b8048160c9d5e866de8abe"}, + {file = "librt-0.7.3-cp313-cp313-win_amd64.whl", hash = "sha256:6b407c23f16ccc36614c136251d6b32bf30de7a57f8e782378f1107be008ddb0"}, + {file = "librt-0.7.3-cp313-cp313-win_arm64.whl", hash = "sha256:abfc57cab3c53c4546aee31859ef06753bfc136c9d208129bad23e2eca39155a"}, + {file = "librt-0.7.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:120dd21d46ff875e849f1aae19346223cf15656be489242fe884036b23d39e93"}, + {file = "librt-0.7.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1617bea5ab31266e152871208502ee943cb349c224846928a1173c864261375e"}, + {file = "librt-0.7.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93b2a1f325fefa1482516ced160c8c7b4b8d53226763fa6c93d151fa25164207"}, + {file = "librt-0.7.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d4801db8354436fd3936531e7f0e4feb411f62433a6b6cb32bb416e20b529f"}, + {file = "librt-0.7.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11ad45122bbed42cfc8b0597450660126ef28fd2d9ae1a219bc5af8406f95678"}, + {file = "librt-0.7.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6b4e7bff1d76dd2b46443078519dc75df1b5e01562345f0bb740cea5266d8218"}, + {file = "librt-0.7.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:d86f94743a11873317094326456b23f8a5788bad9161fd2f0e52088c33564620"}, + {file = "librt-0.7.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:754a0d09997095ad764ccef050dd5bf26cbf457aab9effcba5890dad081d879e"}, + {file = "librt-0.7.3-cp314-cp314-win32.whl", hash = "sha256:fbd7351d43b80d9c64c3cfcb50008f786cc82cba0450e8599fdd64f264320bd3"}, + {file = "librt-0.7.3-cp314-cp314-win_amd64.whl", hash = "sha256:d376a35c6561e81d2590506804b428fc1075fcc6298fc5bb49b771534c0ba010"}, + {file = "librt-0.7.3-cp314-cp314-win_arm64.whl", hash = "sha256:cbdb3f337c88b43c3b49ca377731912c101178be91cb5071aac48faa898e6f8e"}, + {file = "librt-0.7.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9f0e0927efe87cd42ad600628e595a1a0aa1c64f6d0b55f7e6059079a428641a"}, + {file = "librt-0.7.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:020c6db391268bcc8ce75105cb572df8cb659a43fd347366aaa407c366e5117a"}, + {file = "librt-0.7.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7af7785f5edd1f418da09a8cdb9ec84b0213e23d597413e06525340bcce1ea4f"}, + {file = "librt-0.7.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ccadf260bb46a61b9c7e89e2218f6efea9f3eeaaab4e3d1f58571890e54858e"}, + {file = "librt-0.7.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9883b2d819ce83f87ba82a746c81d14ada78784db431e57cc9719179847376e"}, + {file = "librt-0.7.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:59cb0470612d21fa1efddfa0dd710756b50d9c7fb6c1236bbf8ef8529331dc70"}, + {file = "librt-0.7.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:1fe603877e1865b5fd047a5e40379509a4a60204aa7aa0f72b16f7a41c3f0712"}, + {file = "librt-0.7.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5460d99ed30f043595bbdc888f542bad2caeb6226b01c33cda3ae444e8f82d42"}, + {file = "librt-0.7.3-cp314-cp314t-win32.whl", hash = "sha256:d09f677693328503c9e492e33e9601464297c01f9ebd966ea8fc5308f3069bfd"}, + {file = "librt-0.7.3-cp314-cp314t-win_amd64.whl", hash = "sha256:25711f364c64cab2c910a0247e90b51421e45dbc8910ceeb4eac97a9e132fc6f"}, + {file = "librt-0.7.3-cp314-cp314t-win_arm64.whl", hash = "sha256:a9f9b661f82693eb56beb0605156c7fca57f535704ab91837405913417d6990b"}, + {file = "librt-0.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cd8551aa21df6c60baa2624fd086ae7486bdde00c44097b32e1d1b1966e365e0"}, + {file = "librt-0.7.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6eb9295c730e26b849ed1f4022735f36863eb46b14b6e10604c1c39b8b5efaea"}, + {file = "librt-0.7.3-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3edbf257c40d21a42615e9e332a6b10a8bacaaf58250aed8552a14a70efd0d65"}, + {file = "librt-0.7.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b29e97273bd6999e2bfe9fe3531b1f4f64effd28327bced048a33e49b99674a"}, + {file = "librt-0.7.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e40520c37926166c24d0c2e0f3bc3a5f46646c34bdf7b4ea9747c297d6ee809"}, + {file = "librt-0.7.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6bdd9adfca615903578d2060ee8a6eb1c24eaf54919ff0ddc820118e5718931b"}, + {file = "librt-0.7.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f57aca20e637750a2c18d979f7096e2c2033cc40cf7ed201494318de1182f135"}, + {file = "librt-0.7.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cad9971881e4fec00d96af7eaf4b63aa7a595696fc221808b0d3ce7ca9743258"}, + {file = "librt-0.7.3-cp39-cp39-win32.whl", hash = "sha256:170cdb8436188347af17bf9cccf3249ba581c933ed56d926497119d4cf730cec"}, + {file = "librt-0.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:b278a9248a4e3260fee3db7613772ca9ab6763a129d6d6f29555e2f9b168216d"}, + {file = "librt-0.7.3.tar.gz", hash = "sha256:3ec50cf65235ff5c02c5b747748d9222e564ad48597122a361269dd3aa808798"}, +] + [[package]] name = "markdown" -version = "3.7" +version = "3.9" description = "Python implementation of John Gruber's Markdown." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, - {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, + {file = "markdown-3.9-py3-none-any.whl", hash = "sha256:9f4d91ed810864ea88a6f32c07ba8bee1346c0cc1f6b1f9f6c822f2a9667d280"}, + {file = "markdown-3.9.tar.gz", hash = "sha256:d2900fe1782bd33bdbbd56859defef70c2e78fc46668f8eb9df3128138f2cb6a"}, ] [package.dependencies] importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} [package.extras] -docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] testing = ["coverage", "pyyaml"] [[package]] @@ -643,90 +807,121 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" -version = "3.0.2" +version = "3.0.3" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" groups = ["main", "documentation"] files = [ - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, - {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, ] [[package]] name = "matplotlib-inline" -version = "0.1.7" +version = "0.2.1" description = "Inline Matplotlib backend for Jupyter" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, - {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, + {file = "matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76"}, + {file = "matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe"}, ] [package.dependencies] traitlets = "*" +[package.extras] +test = ["flake8", "nbdime", "nbval", "notebook", "pytest"] + [[package]] name = "mdurl" version = "0.1.2" @@ -781,7 +976,7 @@ watchdog = ">=2.0" [package.extras] i18n = ["babel (>=2.9.0)"] -min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] [[package]] name = "mkdocs-get-deps" @@ -803,32 +998,32 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-material" -version = "9.5.50" +version = "9.7.0" description = "Documentation that simply works" optional = false python-versions = ">=3.8" groups = ["documentation"] files = [ - {file = "mkdocs_material-9.5.50-py3-none-any.whl", hash = "sha256:f24100f234741f4d423a9d672a909d859668a4f404796be3cf035f10d6050385"}, - {file = "mkdocs_material-9.5.50.tar.gz", hash = "sha256:ae5fe16f3d7c9ccd05bb6916a7da7420cf99a9ce5e33debd9d40403a090d5825"}, + {file = "mkdocs_material-9.7.0-py3-none-any.whl", hash = "sha256:da2866ea53601125ff5baa8aa06404c6e07af3c5ce3d5de95e3b52b80b442887"}, + {file = "mkdocs_material-9.7.0.tar.gz", hash = "sha256:602b359844e906ee402b7ed9640340cf8a474420d02d8891451733b6b02314ec"}, ] [package.dependencies] -babel = ">=2.10,<3.0" -colorama = ">=0.4,<1.0" -jinja2 = ">=3.0,<4.0" -markdown = ">=3.2,<4.0" -mkdocs = ">=1.6,<2.0" -mkdocs-material-extensions = ">=1.3,<2.0" -paginate = ">=0.5,<1.0" -pygments = ">=2.16,<3.0" -pymdown-extensions = ">=10.2,<11.0" -regex = ">=2022.4" -requests = ">=2.26,<3.0" +babel = ">=2.10" +backrefs = ">=5.7.post1" +colorama = ">=0.4" +jinja2 = ">=3.1" +markdown = ">=3.2" +mkdocs = ">=1.6" +mkdocs-material-extensions = ">=1.3" +paginate = ">=0.5" +pygments = ">=2.16" +pymdown-extensions = ">=10.2" +requests = ">=2.26" [package.extras] git = ["mkdocs-git-committers-plugin-2 (>=1.1,<3)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"] -imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"] +imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<12.0)"] recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"] [[package]] @@ -845,54 +1040,56 @@ files = [ [[package]] name = "mypy" -version = "1.14.1" +version = "1.19.0" description = "Optional static typing for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, - {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, - {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, - {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, - {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, - {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, - {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, - {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, - {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, - {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, - {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, - {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, - {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, - {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, - {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, - {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, + {file = "mypy-1.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6148ede033982a8c5ca1143de34c71836a09f105068aaa8b7d5edab2b053e6c8"}, + {file = "mypy-1.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a9ac09e52bb0f7fb912f5d2a783345c72441a08ef56ce3e17c1752af36340a39"}, + {file = "mypy-1.19.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f7254c15ab3f8ed68f8e8f5cbe88757848df793e31c36aaa4d4f9783fd08ab"}, + {file = "mypy-1.19.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318ba74f75899b0e78b847d8c50821e4c9637c79d9a59680fc1259f29338cb3e"}, + {file = "mypy-1.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf7d84f497f78b682edd407f14a7b6e1a2212b433eedb054e2081380b7395aa3"}, + {file = "mypy-1.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:c3385246593ac2b97f155a0e9639be906e73534630f663747c71908dfbf26134"}, + {file = "mypy-1.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a31e4c28e8ddb042c84c5e977e28a21195d086aaffaf08b016b78e19c9ef8106"}, + {file = "mypy-1.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34ec1ac66d31644f194b7c163d7f8b8434f1b49719d403a5d26c87fff7e913f7"}, + {file = "mypy-1.19.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb64b0ba5980466a0f3f9990d1c582bcab8db12e29815ecb57f1408d99b4bff7"}, + {file = "mypy-1.19.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:120cffe120cca5c23c03c77f84abc0c14c5d2e03736f6c312480020082f1994b"}, + {file = "mypy-1.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a500ab5c444268a70565e374fc803972bfd1f09545b13418a5174e29883dab7"}, + {file = "mypy-1.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:c14a98bc63fd867530e8ec82f217dae29d0550c86e70debc9667fff1ec83284e"}, + {file = "mypy-1.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0fb3115cb8fa7c5f887c8a8d81ccdcb94cff334684980d847e5a62e926910e1d"}, + {file = "mypy-1.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3e19e3b897562276bb331074d64c076dbdd3e79213f36eed4e592272dabd760"}, + {file = "mypy-1.19.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9d491295825182fba01b6ffe2c6fe4e5a49dbf4e2bb4d1217b6ced3b4797bc6"}, + {file = "mypy-1.19.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6016c52ab209919b46169651b362068f632efcd5eb8ef9d1735f6f86da7853b2"}, + {file = "mypy-1.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f188dcf16483b3e59f9278c4ed939ec0254aa8a60e8fc100648d9ab5ee95a431"}, + {file = "mypy-1.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:0e3c3d1e1d62e678c339e7ade72746a9e0325de42cd2cccc51616c7b2ed1a018"}, + {file = "mypy-1.19.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7686ed65dbabd24d20066f3115018d2dce030d8fa9db01aa9f0a59b6813e9f9e"}, + {file = "mypy-1.19.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fd4a985b2e32f23bead72e2fb4bbe5d6aceee176be471243bd831d5b2644672d"}, + {file = "mypy-1.19.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc51a5b864f73a3a182584b1ac75c404396a17eced54341629d8bdcb644a5bba"}, + {file = "mypy-1.19.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:37af5166f9475872034b56c5efdcf65ee25394e9e1d172907b84577120714364"}, + {file = "mypy-1.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:510c014b722308c9bd377993bcbf9a07d7e0692e5fa8fc70e639c1eb19fc6bee"}, + {file = "mypy-1.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:cabbee74f29aa9cd3b444ec2f1e4fa5a9d0d746ce7567a6a609e224429781f53"}, + {file = "mypy-1.19.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f2e36bed3c6d9b5f35d28b63ca4b727cb0228e480826ffc8953d1892ddc8999d"}, + {file = "mypy-1.19.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a18d8abdda14035c5718acb748faec09571432811af129bf0d9e7b2d6699bf18"}, + {file = "mypy-1.19.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75e60aca3723a23511948539b0d7ed514dda194bc3755eae0bfc7a6b4887aa7"}, + {file = "mypy-1.19.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f44f2ae3c58421ee05fe609160343c25f70e3967f6e32792b5a78006a9d850f"}, + {file = "mypy-1.19.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63ea6a00e4bd6822adbfc75b02ab3653a17c02c4347f5bb0cf1d5b9df3a05835"}, + {file = "mypy-1.19.0-cp314-cp314-win_amd64.whl", hash = "sha256:3ad925b14a0bb99821ff6f734553294aa6a3440a8cb082fe1f5b84dfb662afb1"}, + {file = "mypy-1.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0dde5cb375cb94deff0d4b548b993bec52859d1651e073d63a1386d392a95495"}, + {file = "mypy-1.19.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1cf9c59398db1c68a134b0b5354a09a1e124523f00bacd68e553b8bd16ff3299"}, + {file = "mypy-1.19.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3210d87b30e6af9c8faed61be2642fcbe60ef77cec64fa1ef810a630a4cf671c"}, + {file = "mypy-1.19.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2c1101ab41d01303103ab6ef82cbbfedb81c1a060c868fa7cc013d573d37ab5"}, + {file = "mypy-1.19.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ea4fd21bb48f0da49e6d3b37ef6bd7e8228b9fe41bbf4d80d9364d11adbd43c"}, + {file = "mypy-1.19.0-cp39-cp39-win_amd64.whl", hash = "sha256:16f76ff3f3fd8137aadf593cb4607d82634fca675e8211ad75c43d86033ee6c6"}, + {file = "mypy-1.19.0-py3-none-any.whl", hash = "sha256:0c01c99d626380752e527d5ce8e69ffbba2046eb8a060db0329690849cf9b6f9"}, + {file = "mypy-1.19.0.tar.gz", hash = "sha256:f6b874ca77f733222641e5c46e4711648c4037ea13646fd0cdc814c2eaec2528"}, ] [package.dependencies] +librt = ">=0.6.2" mypy_extensions = ">=1.0.0" +pathspec = ">=0.9.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing_extensions = ">=4.6.0" @@ -905,14 +1102,14 @@ reports = ["lxml"] [[package]] name = "mypy-extensions" -version = "1.0.0" +version = "1.1.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" groups = ["linters"] files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] [[package]] @@ -929,14 +1126,14 @@ files = [ [[package]] name = "packaging" -version = "24.2" +version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["main", "dev", "documentation", "test"] files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] [[package]] @@ -957,27 +1154,39 @@ lint = ["black"] [[package]] name = "parso" -version = "0.8.4" +version = "0.8.5" description = "A Python Parser" optional = false python-versions = ">=3.6" groups = ["dev"] files = [ - {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, - {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, + {file = "parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887"}, + {file = "parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a"}, ] [package.extras] qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] testing = ["docopt", "pytest"] +[[package]] +name = "pastel" +version = "0.2.1" +description = "Bring colors to your terminal." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] +files = [ + {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, + {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, +] + [[package]] name = "pathspec" version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" -groups = ["documentation"] +groups = ["documentation", "linters"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -1001,47 +1210,67 @@ ptyprocess = ">=0.5" [[package]] name = "platformdirs" -version = "4.3.6" +version = "4.4.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev", "documentation", "linters"] files = [ - {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, - {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, + {file = "platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85"}, + {file = "platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.11.2)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.14.1)"] [[package]] name = "pluggy" -version = "1.5.0" +version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev", "test"] files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] [package.extras] dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +testing = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "poethepoet" +version = "0.37.0" +description = "A task runner that works well with poetry and uv." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "poethepoet-0.37.0-py3-none-any.whl", hash = "sha256:861790276315abcc8df1b4bd60e28c3d48a06db273edd3092f3c94e1a46e5e22"}, + {file = "poethepoet-0.37.0.tar.gz", hash = "sha256:73edf458707c674a079baa46802e21455bda3a7f82a408e58c31b9f4fe8e933d"}, +] + +[package.dependencies] +pastel = ">=0.2.1,<0.3.0" +pyyaml = ">=6.0.2,<7.0" +tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} + +[package.extras] +poetry-plugin = ["poetry (>=1.2.0,<3.0.0) ; python_version < \"4.0\""] [[package]] name = "pre-commit" -version = "4.1.0" +version = "4.3.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b"}, - {file = "pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4"}, + {file = "pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8"}, + {file = "pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16"}, ] [package.dependencies] @@ -1053,14 +1282,14 @@ virtualenv = ">=20.10.0" [[package]] name = "prompt-toolkit" -version = "3.0.48" +version = "3.0.51" description = "Library for building powerful interactive command lines in Python" optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.8" groups = ["main", "dev"] files = [ - {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, - {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, + {file = "prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07"}, + {file = "prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed"}, ] [package.dependencies] @@ -1096,14 +1325,14 @@ tests = ["pytest"] [[package]] name = "pygments" -version = "2.18.0" +version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["dev", "documentation", "script"] +groups = ["dev", "documentation", "script", "test"] files = [ - {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, - {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, ] [package.extras] @@ -1111,14 +1340,14 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" -version = "10.12" +version = "10.18" description = "Extension pack for Python Markdown." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "pymdown_extensions-10.12-py3-none-any.whl", hash = "sha256:49f81412242d3527b8b4967b990df395c89563043bc51a3d2d7d500e52123b77"}, - {file = "pymdown_extensions-10.12.tar.gz", hash = "sha256:b0ee1e0b2bef1071a47891ab17003bfe5bf824a398e13f49f8ed653b699369a7"}, + {file = "pymdown_extensions-10.18-py3-none-any.whl", hash = "sha256:090bca72be43f7d3186374e23c782899dbef9dc153ef24c59dcd3c346f9ffcae"}, + {file = "pymdown_extensions-10.18.tar.gz", hash = "sha256:20252abe6367354b24191431617a072ee6be9f68c5afcc74ea5573508a61f9e5"}, ] [package.dependencies] @@ -1126,84 +1355,90 @@ markdown = ">=3.6" pyyaml = "*" [package.extras] -extra = ["pygments (>=2.12)"] +extra = ["pygments (>=2.19.1)"] [[package]] name = "pyproject-api" -version = "1.9.0" +version = "1.9.1" description = "API to interact with the python pyproject.toml based projects" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pyproject_api-1.9.0-py3-none-any.whl", hash = "sha256:326df9d68dea22d9d98b5243c46e3ca3161b07a1b9b18e213d1e24fd0e605766"}, - {file = "pyproject_api-1.9.0.tar.gz", hash = "sha256:7e8a9854b2dfb49454fae421cb86af43efbb2b2454e5646ffb7623540321ae6e"}, + {file = "pyproject_api-1.9.1-py3-none-any.whl", hash = "sha256:7d6238d92f8962773dd75b5f0c4a6a27cce092a14b623b811dba656f3b628948"}, + {file = "pyproject_api-1.9.1.tar.gz", hash = "sha256:43c9918f49daab37e302038fc1aed54a8c7a91a9fa935d00b9a485f37e0f5335"}, ] [package.dependencies] -packaging = ">=24.2" +packaging = ">=25" tomli = {version = ">=2.2.1", markers = "python_version < \"3.11\""} [package.extras] -docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=3)"] -testing = ["covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "setuptools (>=75.8)"] +docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=3.2)"] +testing = ["covdefaults (>=2.3)", "pytest (>=8.3.5)", "pytest-cov (>=6.1.1)", "pytest-mock (>=3.14)", "setuptools (>=80.3.1)"] [[package]] name = "pytest" -version = "8.3.4" +version = "8.4.2" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, - {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, ] [package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} +iniconfig = ">=1" +packaging = ">=20" pluggy = ">=1.5,<2" +pygments = ">=2.7.2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" -version = "6.0.0" +version = "7.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, - {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, + {file = "pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861"}, + {file = "pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1"}, ] [package.dependencies] -coverage = {version = ">=7.5", extras = ["toml"]} -pytest = ">=4.6" +coverage = {version = ">=7.10.6", extras = ["toml"]} +pluggy = ">=1.2" +pytest = ">=7" [package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] +testing = ["process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-datadir" -version = "1.5.0" +version = "1.8.0" description = "pytest plugin for test data directories and files" optional = false python-versions = ">=3.8" groups = ["test"] files = [ - {file = "pytest-datadir-1.5.0.tar.gz", hash = "sha256:1617ed92f9afda0c877e4eac91904b5f779d24ba8f5e438752e3ae39d8d2ee3f"}, - {file = "pytest_datadir-1.5.0-py3-none-any.whl", hash = "sha256:34adf361bcc7b37961bbc1dfa8d25a4829e778bab461703c38a5c50ca9c36dc8"}, + {file = "pytest_datadir-1.8.0-py3-none-any.whl", hash = "sha256:5c677bc097d907ac71ca418109adc3abe34cf0bddfe6cf78aecfbabd96a15cf0"}, + {file = "pytest_datadir-1.8.0.tar.gz", hash = "sha256:7a15faed76cebe87cc91941dd1920a9a38eba56a09c11e9ddf1434d28a0f78eb"}, ] [package.dependencies] -pytest = ">=5.0" +pytest = ">=7.0" + +[package.extras] +dev = ["pre-commit", "pytest-datadir[testing]"] +testing = ["pytest", "tox"] [[package]] name = "pytest-freezer" @@ -1223,14 +1458,14 @@ pytest = ">=3.6" [[package]] name = "pytest-mock" -version = "3.14.0" +version = "3.15.1" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, - {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, + {file = "pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d"}, + {file = "pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f"}, ] [package.dependencies] @@ -1241,19 +1476,19 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] [[package]] name = "pytest-regressions" -version = "2.7.0" +version = "2.8.3" description = "Easy to use fixtures to write regression tests." optional = false python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest_regressions-2.7.0-py3-none-any.whl", hash = "sha256:69f5e3f03493cf0ef84d96d23e50a546617c198b1d7746f2e2b9e441cbab4847"}, - {file = "pytest_regressions-2.7.0.tar.gz", hash = "sha256:4c30064e0923929012c94f5d6f35205be06fd8709c7f0dba0228e05c460af05e"}, + {file = "pytest_regressions-2.8.3-py3-none-any.whl", hash = "sha256:72500dd95bde418c850f290a3108dacb56427067f364f7112cb5b16f6d6cc29c"}, + {file = "pytest_regressions-2.8.3.tar.gz", hash = "sha256:1ad90708bee02a3d36c78ef0b6f9692a9a30d312dd828680fd6d2a7235fcd221"}, ] [package.dependencies] pytest = ">=6.2.0" -pytest-datadir = ">=1.2.0" +pytest-datadir = ">=1.7.0" pyyaml = "*" [package.extras] @@ -1264,14 +1499,14 @@ num = ["numpy", "pandas"] [[package]] name = "pytest-xdist" -version = "3.6.1" +version = "3.8.0" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["test"] files = [ - {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, - {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, + {file = "pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88"}, + {file = "pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1"}, ] [package.dependencies] @@ -1300,77 +1535,97 @@ six = ">=1.5" [[package]] name = "pyyaml" -version = "6.0.2" +version = "6.0.3" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["main", "documentation", "linters", "test"] -files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +groups = ["main", "dev", "documentation", "linters", "test"] +files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, ] [[package]] name = "pyyaml-env-tag" -version = "0.1" -description = "A custom YAML tag for referencing environment variables in YAML files. " +version = "1.1" +description = "A custom YAML tag for referencing environment variables in YAML files." optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, - {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, + {file = "pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04"}, + {file = "pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff"}, ] [package.dependencies] @@ -1378,138 +1633,34 @@ pyyaml = "*" [[package]] name = "questionary" -version = "2.1.0" +version = "2.1.1" description = "Python library to build pretty command line user prompts ⭐️" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "questionary-2.1.0-py3-none-any.whl", hash = "sha256:44174d237b68bc828e4878c763a9ad6790ee61990e0ae72927694ead57bab8ec"}, - {file = "questionary-2.1.0.tar.gz", hash = "sha256:6302cdd645b19667d8f6e6634774e9538bfcd1aad9be287e743d96cacaf95587"}, + {file = "questionary-2.1.1-py3-none-any.whl", hash = "sha256:a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59"}, + {file = "questionary-2.1.1.tar.gz", hash = "sha256:3d7e980292bb0107abaa79c68dd3eee3c561b83a0f89ae482860b181c8bd412d"}, ] [package.dependencies] prompt_toolkit = ">=2.0,<4.0" -[[package]] -name = "regex" -version = "2024.11.6" -description = "Alternative regular expression module, to replace re." -optional = false -python-versions = ">=3.8" -groups = ["documentation"] -files = [ - {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, - {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, - {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, - {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, - {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, - {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, - {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, - {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, - {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, - {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, - {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, - {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, - {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, - {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, - {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, - {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, -] - [[package]] name = "requests" -version = "2.32.3" +version = "2.32.5" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, + {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, + {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, ] [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" +charset_normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" @@ -1519,62 +1670,62 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.9.4" +version = "14.2.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" groups = ["script"] files = [ - {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, - {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, + {file = "rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd"}, + {file = "rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.9.4" +version = "0.14.8" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["linters"] files = [ - {file = "ruff-0.9.4-py3-none-linux_armv6l.whl", hash = "sha256:64e73d25b954f71ff100bb70f39f1ee09e880728efb4250c632ceed4e4cdf706"}, - {file = "ruff-0.9.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6ce6743ed64d9afab4fafeaea70d3631b4d4b28b592db21a5c2d1f0ef52934bf"}, - {file = "ruff-0.9.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:54499fb08408e32b57360f6f9de7157a5fec24ad79cb3f42ef2c3f3f728dfe2b"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37c892540108314a6f01f105040b5106aeb829fa5fb0561d2dcaf71485021137"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de9edf2ce4b9ddf43fd93e20ef635a900e25f622f87ed6e3047a664d0e8f810e"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87c90c32357c74f11deb7fbb065126d91771b207bf9bfaaee01277ca59b574ec"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56acd6c694da3695a7461cc55775f3a409c3815ac467279dfa126061d84b314b"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0c93e7d47ed951b9394cf352d6695b31498e68fd5782d6cbc282425655f687a"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d4c8772670aecf037d1bf7a07c39106574d143b26cfe5ed1787d2f31e800214"}, - {file = "ruff-0.9.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfc5f1d7afeda8d5d37660eeca6d389b142d7f2b5a1ab659d9214ebd0e025231"}, - {file = "ruff-0.9.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:faa935fc00ae854d8b638c16a5f1ce881bc3f67446957dd6f2af440a5fc8526b"}, - {file = "ruff-0.9.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a6c634fc6f5a0ceae1ab3e13c58183978185d131a29c425e4eaa9f40afe1e6d6"}, - {file = "ruff-0.9.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:433dedf6ddfdec7f1ac7575ec1eb9844fa60c4c8c2f8887a070672b8d353d34c"}, - {file = "ruff-0.9.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d612dbd0f3a919a8cc1d12037168bfa536862066808960e0cc901404b77968f0"}, - {file = "ruff-0.9.4-py3-none-win32.whl", hash = "sha256:db1192ddda2200671f9ef61d9597fcef89d934f5d1705e571a93a67fb13a4402"}, - {file = "ruff-0.9.4-py3-none-win_amd64.whl", hash = "sha256:05bebf4cdbe3ef75430d26c375773978950bbf4ee3c95ccb5448940dc092408e"}, - {file = "ruff-0.9.4-py3-none-win_arm64.whl", hash = "sha256:585792f1e81509e38ac5123492f8875fbc36f3ede8185af0a26df348e5154f41"}, - {file = "ruff-0.9.4.tar.gz", hash = "sha256:6907ee3529244bb0ed066683e075f09285b38dd5b4039370df6ff06041ca19e7"}, + {file = "ruff-0.14.8-py3-none-linux_armv6l.whl", hash = "sha256:ec071e9c82eca417f6111fd39f7043acb53cd3fde9b1f95bbed745962e345afb"}, + {file = "ruff-0.14.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8cdb162a7159f4ca36ce980a18c43d8f036966e7f73f866ac8f493b75e0c27e9"}, + {file = "ruff-0.14.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2e2fcbefe91f9fad0916850edf0854530c15bd1926b6b779de47e9ab619ea38f"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9d70721066a296f45786ec31916dc287b44040f553da21564de0ab4d45a869b"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c87e09b3cd9d126fc67a9ecd3b5b1d3ded2b9c7fce3f16e315346b9d05cfb52"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d62cb310c4fbcb9ee4ac023fe17f984ae1e12b8a4a02e3d21489f9a2a5f730c"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1af35c2d62633d4da0521178e8a2641c636d2a7153da0bac1b30cfd4ccd91344"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:25add4575ffecc53d60eed3f24b1e934493631b48ebbc6ebaf9d8517924aca4b"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c943d847b7f02f7db4201a0600ea7d244d8a404fbb639b439e987edcf2baf9a"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb6e8bf7b4f627548daa1b69283dac5a296bfe9ce856703b03130732e20ddfe2"}, + {file = "ruff-0.14.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7aaf2974f378e6b01d1e257c6948207aec6a9b5ba53fab23d0182efb887a0e4a"}, + {file = "ruff-0.14.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e5758ca513c43ad8a4ef13f0f081f80f08008f410790f3611a21a92421ab045b"}, + {file = "ruff-0.14.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f74f7ba163b6e85a8d81a590363bf71618847e5078d90827749bfda1d88c9cdf"}, + {file = "ruff-0.14.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eed28f6fafcc9591994c42254f5a5c5ca40e69a30721d2ab18bb0bb3baac3ab6"}, + {file = "ruff-0.14.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:21d48fa744c9d1cb8d71eb0a740c4dd02751a5de9db9a730a8ef75ca34cf138e"}, + {file = "ruff-0.14.8-py3-none-win32.whl", hash = "sha256:15f04cb45c051159baebb0f0037f404f1dc2f15a927418f29730f411a79bc4e7"}, + {file = "ruff-0.14.8-py3-none-win_amd64.whl", hash = "sha256:9eeb0b24242b5bbff3011409a739929f497f3fb5fe3b5698aba5e77e8c833097"}, + {file = "ruff-0.14.8-py3-none-win_arm64.whl", hash = "sha256:965a582c93c63fe715fd3e3f8aa37c4b776777203d8e1d8aa3cc0c14424a4b99"}, + {file = "ruff-0.14.8.tar.gz", hash = "sha256:774ed0dd87d6ce925e3b8496feb3a00ac564bea52b9feb551ecd17e0a23d1eed"}, ] [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -groups = ["dev", "documentation", "test"] +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["documentation", "test"] files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] @@ -1599,14 +1750,14 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "termcolor" -version = "2.5.0" +version = "3.1.0" description = "ANSI color formatting for output in terminal" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8"}, - {file = "termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f"}, + {file = "termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa"}, + {file = "termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970"}, ] [package.extras] @@ -1614,86 +1765,93 @@ tests = ["pytest", "pytest-cov"] [[package]] name = "tomli" -version = "2.2.1" +version = "2.3.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["dev", "linters", "test"] files = [ - {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, - {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, - {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, - {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, - {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, - {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, - {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, - {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, - {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, - {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, + {file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"}, + {file = "tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c"}, + {file = "tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456"}, + {file = "tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6"}, + {file = "tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876"}, + {file = "tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05"}, + {file = "tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606"}, + {file = "tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005"}, + {file = "tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463"}, + {file = "tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f"}, + {file = "tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0"}, + {file = "tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba"}, + {file = "tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b"}, + {file = "tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549"}, ] markers = {dev = "python_version < \"3.11\"", linters = "python_version < \"3.11\"", test = "python_full_version <= \"3.11.0a6\""} [[package]] name = "tomlkit" -version = "0.13.2" +version = "0.13.3" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, - {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, + {file = "tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0"}, + {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"}, ] [[package]] name = "tox" -version = "4.24.1" +version = "4.30.3" description = "tox is a generic virtualenv management and test command line tool" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "tox-4.24.1-py3-none-any.whl", hash = "sha256:57ba7df7d199002c6df8c2db9e6484f3de6ca8f42013c083ea2d4d1e5c6bdc75"}, - {file = "tox-4.24.1.tar.gz", hash = "sha256:083a720adbc6166fff0b7d1df9d154f9d00bfccb9403b8abf6bc0ee435d6a62e"}, + {file = "tox-4.30.3-py3-none-any.whl", hash = "sha256:a9f17b4b2d0f74fe0d76207236925a119095011e5c2e661a133115a8061178c9"}, + {file = "tox-4.30.3.tar.gz", hash = "sha256:f3dd0735f1cd4e8fbea5a3661b77f517456b5f0031a6256432533900e34b90bf"}, ] [package.dependencies] -cachetools = ">=5.5" +cachetools = ">=6.1" chardet = ">=5.2" colorama = ">=0.4.6" -filelock = ">=3.16.1" -packaging = ">=24.2" -platformdirs = ">=4.3.6" -pluggy = ">=1.5" -pyproject-api = ">=1.8" -tomli = {version = ">=2.1", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.12.2", markers = "python_version < \"3.11\""} -virtualenv = ">=20.27.1" - -[package.extras] -test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.3)", "pytest-mock (>=3.14)"] +filelock = ">=3.18" +packaging = ">=25" +platformdirs = ">=4.3.8" +pluggy = ">=1.6" +pyproject-api = ">=1.9.1" +tomli = {version = ">=2.2.1", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.14.1", markers = "python_version < \"3.11\""} +virtualenv = ">=20.31.2" [[package]] name = "traitlets" @@ -1711,105 +1869,118 @@ files = [ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] +[[package]] +name = "types-colorama" +version = "0.4.15.20250801" +description = "Typing stubs for colorama" +optional = false +python-versions = ">=3.9" +groups = ["linters"] +files = [ + {file = "types_colorama-0.4.15.20250801-py3-none-any.whl", hash = "sha256:b6e89bd3b250fdad13a8b6a465c933f4a5afe485ea2e2f104d739be50b13eea9"}, + {file = "types_colorama-0.4.15.20250801.tar.gz", hash = "sha256:02565d13d68963d12237d3f330f5ecd622a3179f7b5b14ee7f16146270c357f5"}, +] + [[package]] name = "types-deprecated" -version = "1.2.15.20241117" +version = "1.3.1.20251101" description = "Typing stubs for Deprecated" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "types-Deprecated-1.2.15.20241117.tar.gz", hash = "sha256:924002c8b7fddec51ba4949788a702411a2e3636cd9b2a33abd8ee119701d77e"}, - {file = "types_Deprecated-1.2.15.20241117-py3-none-any.whl", hash = "sha256:a0cc5e39f769fc54089fd8e005416b55d74aa03f6964d2ed1a0b0b2e28751884"}, + {file = "types_deprecated-1.3.1.20251101-py3-none-any.whl", hash = "sha256:274edcc2a084d3fe31802d3c1379abd630716d3db34e40577e12ad84d6b73134"}, + {file = "types_deprecated-1.3.1.20251101.tar.gz", hash = "sha256:f002d266b73201f46ec6fc712c1f016067ec6cb44357559cdb50c86b010951a7"}, ] [[package]] name = "types-python-dateutil" -version = "2.9.0.20241206" +version = "2.9.0.20251115" description = "Typing stubs for python-dateutil" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, - {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, + {file = "types_python_dateutil-2.9.0.20251115-py3-none-any.whl", hash = "sha256:9cf9c1c582019753b8639a081deefd7e044b9fa36bd8217f565c6c4e36ee0624"}, + {file = "types_python_dateutil-2.9.0.20251115.tar.gz", hash = "sha256:8a47f2c3920f52a994056b8786309b43143faa5a64d4cbb2722d6addabdf1a58"}, ] [[package]] name = "types-pyyaml" -version = "6.0.12.20241230" +version = "6.0.12.20250915" description = "Typing stubs for PyYAML" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["linters"] files = [ - {file = "types_PyYAML-6.0.12.20241230-py3-none-any.whl", hash = "sha256:fa4d32565219b68e6dee5f67534c722e53c00d1cfc09c435ef04d7353e1e96e6"}, - {file = "types_pyyaml-6.0.12.20241230.tar.gz", hash = "sha256:7f07622dbd34bb9c8b264fe860a17e0efcad00d50b5f27e93984909d9363498c"}, + {file = "types_pyyaml-6.0.12.20250915-py3-none-any.whl", hash = "sha256:e7d4d9e064e89a3b3cae120b4990cd370874d2bf12fa5f46c97018dd5d3c9ab6"}, + {file = "types_pyyaml-6.0.12.20250915.tar.gz", hash = "sha256:0f8b54a528c303f0e6f7165687dd33fafa81c807fcac23f632b63aa624ced1d3"}, ] [[package]] name = "types-termcolor" -version = "0.1.1" +version = "1.1.6.2" description = "Typing stubs for termcolor" optional = false python-versions = "*" groups = ["linters"] files = [ - {file = "types-termcolor-0.1.1.tar.gz", hash = "sha256:4d9e09ce7f3267985f5280b22e25790c98cb64628b6466e1fb915dbb52ad7136"}, - {file = "types_termcolor-0.1.1-py2.py3-none-any.whl", hash = "sha256:3694c312e32f71fdc0f469c334ea21645f3130d90c93cd53bcb06b1233e174d5"}, + {file = "types-termcolor-1.1.6.2.tar.gz", hash = "sha256:d8f0f69cf5552cc59ce75aa5172937cec9b320c17453adefe4168b93d16daad6"}, + {file = "types_termcolor-1.1.6.2-py3-none-any.whl", hash = "sha256:44c4c762c54a90d99b5c1033ef008aaa5610056d31d5c66b9288a942682a64d7"}, ] [[package]] name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" optional = false -python-versions = ">=3.8" -groups = ["main", "dev", "linters", "script"] +python-versions = ">=3.9" +groups = ["main", "dev", "linters", "test"] files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] -markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.11\"", script = "python_version < \"3.11\""} +markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.11\"", test = "python_version < \"3.11\""} [[package]] name = "urllib3" -version = "2.2.3" +version = "2.6.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["documentation"] files = [ - {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, - {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, + {file = "urllib3-2.6.1-py3-none-any.whl", hash = "sha256:e67d06fe947c36a7ca39f4994b08d73922d40e6cca949907be05efa6fd75110b"}, + {file = "urllib3-2.6.1.tar.gz", hash = "sha256:5379eb6e1aba4088bae84f8242960017ec8d8e3decf30480b3a1abdaa9671a3f"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [[package]] name = "virtualenv" -version = "20.27.1" +version = "20.35.4" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" groups = ["dev", "linters"] files = [ - {file = "virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4"}, - {file = "virtualenv-20.27.1.tar.gz", hash = "sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba"}, + {file = "virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b"}, + {file = "virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c"}, ] [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" platformdirs = ">=3.9.1,<5" +typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\""} [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] [[package]] name = "watchdog" @@ -1856,113 +2027,158 @@ watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "wcwidth" -version = "0.2.13" +version = "0.2.14" description = "Measures the displayed width of unicode strings in a terminal" optional = false -python-versions = "*" +python-versions = ">=3.6" groups = ["main", "dev"] files = [ - {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, - {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, + {file = "wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1"}, + {file = "wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605"}, ] [[package]] name = "wrapt" -version = "1.17.0" +version = "2.0.1" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" -groups = ["test"] +groups = ["main"] files = [ - {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"}, - {file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"}, - {file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"}, - {file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"}, - {file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"}, - {file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"}, - {file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"}, - {file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"}, - {file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"}, - {file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"}, - {file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"}, - {file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"}, - {file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"}, - {file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"}, - {file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"}, - {file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"}, - {file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"}, - {file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"}, - {file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"}, - {file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"}, - {file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"}, - {file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"}, - {file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"}, + {file = "wrapt-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:64b103acdaa53b7caf409e8d45d39a8442fe6dcfec6ba3f3d141e0cc2b5b4dbd"}, + {file = "wrapt-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91bcc576260a274b169c3098e9a3519fb01f2989f6d3d386ef9cbf8653de1374"}, + {file = "wrapt-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab594f346517010050126fcd822697b25a7031d815bb4fbc238ccbe568216489"}, + {file = "wrapt-2.0.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36982b26f190f4d737f04a492a68accbfc6fa042c3f42326fdfbb6c5b7a20a31"}, + {file = "wrapt-2.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23097ed8bc4c93b7bf36fa2113c6c733c976316ce0ee2c816f64ca06102034ef"}, + {file = "wrapt-2.0.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bacfe6e001749a3b64db47bcf0341da757c95959f592823a93931a422395013"}, + {file = "wrapt-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8ec3303e8a81932171f455f792f8df500fc1a09f20069e5c16bd7049ab4e8e38"}, + {file = "wrapt-2.0.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:3f373a4ab5dbc528a94334f9fe444395b23c2f5332adab9ff4ea82f5a9e33bc1"}, + {file = "wrapt-2.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f49027b0b9503bf6c8cdc297ca55006b80c2f5dd36cecc72c6835ab6e10e8a25"}, + {file = "wrapt-2.0.1-cp310-cp310-win32.whl", hash = "sha256:8330b42d769965e96e01fa14034b28a2a7600fbf7e8f0cc90ebb36d492c993e4"}, + {file = "wrapt-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:1218573502a8235bb8a7ecaed12736213b22dcde9feab115fa2989d42b5ded45"}, + {file = "wrapt-2.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:eda8e4ecd662d48c28bb86be9e837c13e45c58b8300e43ba3c9b4fa9900302f7"}, + {file = "wrapt-2.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0e17283f533a0d24d6e5429a7d11f250a58d28b4ae5186f8f47853e3e70d2590"}, + {file = "wrapt-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85df8d92158cb8f3965aecc27cf821461bb5f40b450b03facc5d9f0d4d6ddec6"}, + {file = "wrapt-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1be685ac7700c966b8610ccc63c3187a72e33cab53526a27b2a285a662cd4f7"}, + {file = "wrapt-2.0.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:df0b6d3b95932809c5b3fecc18fda0f1e07452d05e2662a0b35548985f256e28"}, + {file = "wrapt-2.0.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da7384b0e5d4cae05c97cd6f94faaf78cc8b0f791fc63af43436d98c4ab37bb"}, + {file = "wrapt-2.0.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ec65a78fbd9d6f083a15d7613b2800d5663dbb6bb96003899c834beaa68b242c"}, + {file = "wrapt-2.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7de3cc939be0e1174969f943f3b44e0d79b6f9a82198133a5b7fc6cc92882f16"}, + {file = "wrapt-2.0.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:fb1a5b72cbd751813adc02ef01ada0b0d05d3dcbc32976ce189a1279d80ad4a2"}, + {file = "wrapt-2.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3fa272ca34332581e00bf7773e993d4f632594eb2d1b0b162a9038df0fd971dd"}, + {file = "wrapt-2.0.1-cp311-cp311-win32.whl", hash = "sha256:fc007fdf480c77301ab1afdbb6ab22a5deee8885f3b1ed7afcb7e5e84a0e27be"}, + {file = "wrapt-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:47434236c396d04875180171ee1f3815ca1eada05e24a1ee99546320d54d1d1b"}, + {file = "wrapt-2.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:837e31620e06b16030b1d126ed78e9383815cbac914693f54926d816d35d8edf"}, + {file = "wrapt-2.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1fdbb34da15450f2b1d735a0e969c24bdb8d8924892380126e2a293d9902078c"}, + {file = "wrapt-2.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3d32794fe940b7000f0519904e247f902f0149edbe6316c710a8562fb6738841"}, + {file = "wrapt-2.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:386fb54d9cd903ee0012c09291336469eb7b244f7183d40dc3e86a16a4bace62"}, + {file = "wrapt-2.0.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7b219cb2182f230676308cdcacd428fa837987b89e4b7c5c9025088b8a6c9faf"}, + {file = "wrapt-2.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:641e94e789b5f6b4822bb8d8ebbdfc10f4e4eae7756d648b717d980f657a9eb9"}, + {file = "wrapt-2.0.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe21b118b9f58859b5ebaa4b130dee18669df4bd111daad082b7beb8799ad16b"}, + {file = "wrapt-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:17fb85fa4abc26a5184d93b3efd2dcc14deb4b09edcdb3535a536ad34f0b4dba"}, + {file = "wrapt-2.0.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:b89ef9223d665ab255ae42cc282d27d69704d94be0deffc8b9d919179a609684"}, + {file = "wrapt-2.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a453257f19c31b31ba593c30d997d6e5be39e3b5ad9148c2af5a7314061c63eb"}, + {file = "wrapt-2.0.1-cp312-cp312-win32.whl", hash = "sha256:3e271346f01e9c8b1130a6a3b0e11908049fe5be2d365a5f402778049147e7e9"}, + {file = "wrapt-2.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:2da620b31a90cdefa9cd0c2b661882329e2e19d1d7b9b920189956b76c564d75"}, + {file = "wrapt-2.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:aea9c7224c302bc8bfc892b908537f56c430802560e827b75ecbde81b604598b"}, + {file = "wrapt-2.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:47b0f8bafe90f7736151f61482c583c86b0693d80f075a58701dd1549b0010a9"}, + {file = "wrapt-2.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cbeb0971e13b4bd81d34169ed57a6dda017328d1a22b62fda45e1d21dd06148f"}, + {file = "wrapt-2.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb7cffe572ad0a141a7886a1d2efa5bef0bf7fe021deeea76b3ab334d2c38218"}, + {file = "wrapt-2.0.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8d60527d1ecfc131426b10d93ab5d53e08a09c5fa0175f6b21b3252080c70a9"}, + {file = "wrapt-2.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c654eafb01afac55246053d67a4b9a984a3567c3808bb7df2f8de1c1caba2e1c"}, + {file = "wrapt-2.0.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:98d873ed6c8b4ee2418f7afce666751854d6d03e3c0ec2a399bb039cd2ae89db"}, + {file = "wrapt-2.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9e850f5b7fc67af856ff054c71690d54fa940c3ef74209ad9f935b4f66a0233"}, + {file = "wrapt-2.0.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e505629359cb5f751e16e30cf3f91a1d3ddb4552480c205947da415d597f7ac2"}, + {file = "wrapt-2.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2879af909312d0baf35f08edeea918ee3af7ab57c37fe47cb6a373c9f2749c7b"}, + {file = "wrapt-2.0.1-cp313-cp313-win32.whl", hash = "sha256:d67956c676be5a24102c7407a71f4126d30de2a569a1c7871c9f3cabc94225d7"}, + {file = "wrapt-2.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:9ca66b38dd642bf90c59b6738af8070747b610115a39af2498535f62b5cdc1c3"}, + {file = "wrapt-2.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:5a4939eae35db6b6cec8e7aa0e833dcca0acad8231672c26c2a9ab7a0f8ac9c8"}, + {file = "wrapt-2.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a52f93d95c8d38fed0669da2ebdb0b0376e895d84596a976c15a9eb45e3eccb3"}, + {file = "wrapt-2.0.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e54bbf554ee29fcceee24fa41c4d091398b911da6e7f5d7bffda963c9aed2e1"}, + {file = "wrapt-2.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:908f8c6c71557f4deaa280f55d0728c3bca0960e8c3dd5ceeeafb3c19942719d"}, + {file = "wrapt-2.0.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e2f84e9af2060e3904a32cea9bb6db23ce3f91cfd90c6b426757cf7cc01c45c7"}, + {file = "wrapt-2.0.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3612dc06b436968dfb9142c62e5dfa9eb5924f91120b3c8ff501ad878f90eb3"}, + {file = "wrapt-2.0.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d2d947d266d99a1477cd005b23cbd09465276e302515e122df56bb9511aca1b"}, + {file = "wrapt-2.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7d539241e87b650cbc4c3ac9f32c8d1ac8a54e510f6dca3f6ab60dcfd48c9b10"}, + {file = "wrapt-2.0.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:4811e15d88ee62dbf5c77f2c3ff3932b1e3ac92323ba3912f51fc4016ce81ecf"}, + {file = "wrapt-2.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c1c91405fcf1d501fa5d55df21e58ea49e6b879ae829f1039faaf7e5e509b41e"}, + {file = "wrapt-2.0.1-cp313-cp313t-win32.whl", hash = "sha256:e76e3f91f864e89db8b8d2a8311d57df93f01ad6bb1e9b9976d1f2e83e18315c"}, + {file = "wrapt-2.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:83ce30937f0ba0d28818807b303a412440c4b63e39d3d8fc036a94764b728c92"}, + {file = "wrapt-2.0.1-cp313-cp313t-win_arm64.whl", hash = "sha256:4b55cacc57e1dc2d0991dbe74c6419ffd415fb66474a02335cb10efd1aa3f84f"}, + {file = "wrapt-2.0.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:5e53b428f65ece6d9dad23cb87e64506392b720a0b45076c05354d27a13351a1"}, + {file = "wrapt-2.0.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ad3ee9d0f254851c71780966eb417ef8e72117155cff04821ab9b60549694a55"}, + {file = "wrapt-2.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7b822c61ed04ee6ad64bc90d13368ad6eb094db54883b5dde2182f67a7f22c0"}, + {file = "wrapt-2.0.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7164a55f5e83a9a0b031d3ffab4d4e36bbec42e7025db560f225489fa929e509"}, + {file = "wrapt-2.0.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e60690ba71a57424c8d9ff28f8d006b7ad7772c22a4af432188572cd7fa004a1"}, + {file = "wrapt-2.0.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3cd1a4bd9a7a619922a8557e1318232e7269b5fb69d4ba97b04d20450a6bf970"}, + {file = "wrapt-2.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b4c2e3d777e38e913b8ce3a6257af72fb608f86a1df471cb1d4339755d0a807c"}, + {file = "wrapt-2.0.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3d366aa598d69416b5afedf1faa539fac40c1d80a42f6b236c88c73a3c8f2d41"}, + {file = "wrapt-2.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c235095d6d090aa903f1db61f892fffb779c1eaeb2a50e566b52001f7a0f66ed"}, + {file = "wrapt-2.0.1-cp314-cp314-win32.whl", hash = "sha256:bfb5539005259f8127ea9c885bdc231978c06b7a980e63a8a61c8c4c979719d0"}, + {file = "wrapt-2.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:4ae879acc449caa9ed43fc36ba08392b9412ee67941748d31d94e3cedb36628c"}, + {file = "wrapt-2.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:8639b843c9efd84675f1e100ed9e99538ebea7297b62c4b45a7042edb84db03e"}, + {file = "wrapt-2.0.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:9219a1d946a9b32bb23ccae66bdb61e35c62773ce7ca6509ceea70f344656b7b"}, + {file = "wrapt-2.0.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fa4184e74197af3adad3c889a1af95b53bb0466bced92ea99a0c014e48323eec"}, + {file = "wrapt-2.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c5ef2f2b8a53b7caee2f797ef166a390fef73979b15778a4a153e4b5fedce8fa"}, + {file = "wrapt-2.0.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e042d653a4745be832d5aa190ff80ee4f02c34b21f4b785745eceacd0907b815"}, + {file = "wrapt-2.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2afa23318136709c4b23d87d543b425c399887b4057936cd20386d5b1422b6fa"}, + {file = "wrapt-2.0.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c72328f668cf4c503ffcf9434c2b71fdd624345ced7941bc6693e61bbe36bef"}, + {file = "wrapt-2.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3793ac154afb0e5b45d1233cb94d354ef7a983708cc3bb12563853b1d8d53747"}, + {file = "wrapt-2.0.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fec0d993ecba3991645b4857837277469c8cc4c554a7e24d064d1ca291cfb81f"}, + {file = "wrapt-2.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:949520bccc1fa227274da7d03bf238be15389cd94e32e4297b92337df9b7a349"}, + {file = "wrapt-2.0.1-cp314-cp314t-win32.whl", hash = "sha256:be9e84e91d6497ba62594158d3d31ec0486c60055c49179edc51ee43d095f79c"}, + {file = "wrapt-2.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:61c4956171c7434634401db448371277d07032a81cc21c599c22953374781395"}, + {file = "wrapt-2.0.1-cp314-cp314t-win_arm64.whl", hash = "sha256:35cdbd478607036fee40273be8ed54a451f5f23121bd9d4be515158f9498f7ad"}, + {file = "wrapt-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:90897ea1cf0679763b62e79657958cd54eae5659f6360fc7d2ccc6f906342183"}, + {file = "wrapt-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:50844efc8cdf63b2d90cd3d62d4947a28311e6266ce5235a219d21b195b4ec2c"}, + {file = "wrapt-2.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49989061a9977a8cbd6d20f2efa813f24bf657c6990a42967019ce779a878dbf"}, + {file = "wrapt-2.0.1-cp38-cp38-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:09c7476ab884b74dce081ad9bfd07fe5822d8600abade571cb1f66d5fc915af6"}, + {file = "wrapt-2.0.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1a8a09a004ef100e614beec82862d11fc17d601092c3599afd22b1f36e4137e"}, + {file = "wrapt-2.0.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:89a82053b193837bf93c0f8a57ded6e4b6d88033a499dadff5067e912c2a41e9"}, + {file = "wrapt-2.0.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f26f8e2ca19564e2e1fdbb6a0e47f36e0efbab1acc31e15471fad88f828c75f6"}, + {file = "wrapt-2.0.1-cp38-cp38-win32.whl", hash = "sha256:115cae4beed3542e37866469a8a1f2b9ec549b4463572b000611e9946b86e6f6"}, + {file = "wrapt-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:c4012a2bd37059d04f8209916aa771dfb564cccb86079072bdcd48a308b6a5c5"}, + {file = "wrapt-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:68424221a2dc00d634b54f92441914929c5ffb1c30b3b837343978343a3512a3"}, + {file = "wrapt-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6bd1a18f5a797fe740cb3d7a0e853a8ce6461cc62023b630caec80171a6b8097"}, + {file = "wrapt-2.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fb3a86e703868561c5cad155a15c36c716e1ab513b7065bd2ac8ed353c503333"}, + {file = "wrapt-2.0.1-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5dc1b852337c6792aa111ca8becff5bacf576bf4a0255b0f05eb749da6a1643e"}, + {file = "wrapt-2.0.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c046781d422f0830de6329fa4b16796096f28a92c8aef3850674442cdcb87b7f"}, + {file = "wrapt-2.0.1-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f73f9f7a0ebd0db139253d27e5fc8d2866ceaeef19c30ab5d69dcbe35e1a6981"}, + {file = "wrapt-2.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b667189cf8efe008f55bbda321890bef628a67ab4147ebf90d182f2dadc78790"}, + {file = "wrapt-2.0.1-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:a9a83618c4f0757557c077ef71d708ddd9847ed66b7cc63416632af70d3e2308"}, + {file = "wrapt-2.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e9b121e9aeb15df416c2c960b8255a49d44b4038016ee17af03975992d03931"}, + {file = "wrapt-2.0.1-cp39-cp39-win32.whl", hash = "sha256:1f186e26ea0a55f809f232e92cc8556a0977e00183c3ebda039a807a42be1494"}, + {file = "wrapt-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:bf4cb76f36be5de950ce13e22e7fdf462b35b04665a12b64f3ac5c1bbbcf3728"}, + {file = "wrapt-2.0.1-cp39-cp39-win_arm64.whl", hash = "sha256:d6cc985b9c8b235bd933990cdbf0f891f8e010b65a3911f7a55179cd7b0fc57b"}, + {file = "wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca"}, + {file = "wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f"}, ] +[package.extras] +dev = ["pytest", "setuptools"] + [[package]] name = "zipp" -version = "3.21.0" +version = "3.23.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" groups = ["main", "documentation"] -markers = "python_version < \"3.10\"" +markers = "python_version == \"3.9\"" files = [ - {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, - {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, + {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, + {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "e15b424a0569f939e297c8abfcf09753f1fbcc5b4ad891163cc0982accd3b372" +content-hash = "8f4edbdaff3ae48098899feacba7ea3764bdf2d960b78b8f5e8c39b19cc8c2e1" diff --git a/pyproject.toml b/pyproject.toml index 92b28fd091..3e9c45ae63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,30 +1,33 @@ [project] name = "commitizen" -version = "4.6.3" +version = "4.10.1" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ { name = "Wei Lee", email = "weilee.rx@gmail.com" }, { name = "Axel H.", email = "noirbizarre@gmail.com" }, + { name = "Tim Hsiung", email = "bear890707@gmail.com" }, ] license = { file = "LICENSE" } readme = "docs/README.md" requires-python = ">=3.9,<4.0" dependencies = [ "questionary (>=2.0,<3.0)", + # Exclude transitive dependency due to known issue in questionary: https://github.com/tmbo/questionary/issues/454 + "prompt_toolkit!=3.0.52", "decli (>=0.6.0,<1.0)", "colorama (>=0.4.1,<1.0)", - "termcolor (>=1.1,<3)", + "termcolor (>=1.1.0,<4.0.0)", "packaging>=19", - "tomlkit (>=0.5.3,<1.0.0)", + "tomlkit (>=0.8.0,<1.0.0)", "jinja2>=2.10.3", "pyyaml>=3.08", "argcomplete >=1.12.1,<3.7", "typing-extensions (>=4.0.1,<5.0.0) ; python_version < '3.11'", "charset-normalizer (>=2.1.0,<4)", + "deprecated (>=1.2.13, <2)", # Use the Python 3.11 and 3.12 compatible API: https://github.com/python/importlib_metadata#compatibility - "importlib_metadata (>=8.0.0,<9) ; python_version < '3.10'", - + "importlib-metadata >=8.0.0,<8.7.0 ; python_version < '3.10'", ] keywords = ["commitizen", "conventional", "commits", "git"] # See also: https://pypi.org/classifiers/ @@ -82,13 +85,43 @@ pep440 = "commitizen.version_schemes:Pep440" semver = "commitizen.version_schemes:SemVer" semver2 = "commitizen.version_schemes:SemVer2" +[dependency-groups] +dev = ["ipython>=8.0", "tox>4", "poethepoet>=0.34.0"] + +test = [ + "pytest>=7.2", + "pytest-cov>=4", + "pytest-mock>=3.10", + "pytest-regressions>=2.4.0", + "pytest-freezer>=0.4.6", + "pytest-xdist>=3.1.0", +] + +linters = [ + "ruff>=0.11.5", + "pre-commit>=3.2.0", + "mypy>=1.16.0", + "types-deprecated>=1.2.9.2", + "types-python-dateutil>=2.8.19.13", + "types-PyYAML>=5.4.3", + "types-termcolor>=0.1.1", + "types-colorama>=0.4.15.20240311", +] + +documentation = ["mkdocs>=1.4.2", "mkdocs-material>=9.1.6"] + +script = [ + # for scripts/gen_cli_help_screenshots.py + "rich>=13.7.1", +] + [build-system] -requires = ["poetry-core>=2.0"] +requires = ["poetry-core>=2.2"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "4.6.3" +version = "4.10.1" tag_format = "v$version" version_files = [ "pyproject.toml:version", @@ -104,37 +137,6 @@ packages = [{ include = "commitizen" }, { include = "commitizen/py.typed" }] [tool.poetry.requires-plugins] "poethepoet" = ">=0.32.2" -[tool.poetry.group.dev.dependencies] -ipython = "^8.0" -tox = ">4" - -[tool.poetry.group.test.dependencies] -pytest = ">=7.2,<9.0" -pytest-cov = ">=4,<7" -pytest-mock = "^3.10" -pytest-regressions = "^2.4.0" -pytest-freezer = "^0.4.6" -pytest-xdist = "^3.1.0" -deprecated = "^1.2.13" - -[tool.poetry.group.linters.dependencies] -ruff = ">=0.5.0,<0.10.0" -pre-commit = ">=2.18,<5.0" -mypy = "^1.4" -types-deprecated = "^1.2.9.2" -types-python-dateutil = "^2.8.19.13" -types-PyYAML = ">=5.4.3,<7.0.0" -types-termcolor = "^0.1.1" - -[tool.poetry.group.documentation.dependencies] -mkdocs = "^1.4.2" -mkdocs-material = "^9.1.6" - -[tool.poetry.group.script.dependencies] -# for scripts/gen_cli_help_screenshots.py -rich = "^13.7.1" - - [tool.coverage] [tool.coverage.report] show_missing = true @@ -171,7 +173,7 @@ testpaths = ["tests/"] [tool.tox] requires = ["tox>=4.22"] -env_list = ["3.9", "3.10", "3.11", "3.12", "3.13"] +env_list = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] [tool.tox.env_run_base] description = "Run tests suite against Python {base_python}" @@ -181,10 +183,14 @@ commands_pre = [["poetry", "install", "--only", "main,test"]] commands = [["pytest", { replace = "posargs", extend = true }]] [tool.ruff] +required-version = ">=0.11.5" line-length = 88 [tool.ruff.lint] select = [ + # flake8-annotations + "ANN001", + "ANN2", # pycodestyle "E", # Pyflakes @@ -193,9 +199,28 @@ select = [ "UP", # isort "I", + # pygrep-hooks + "PGH003", + "PGH004", + # unsorted-dunder-all + "RUF022", + # unused-noqa + "RUF100", + # Checks for uses of the assert keyword. + "S101", + # flake8-type-checking (TC) + "TC001", + "TC002", + "TC003", + "TC004", + "TC005", + "TC006", ] ignore = ["E501", "D1", "D415"] +[tool.ruff.lint.per-file-ignores] +"tests/*" = ["ANN", "S101"] + [tool.ruff.lint.isort] known-first-party = ["commitizen", "tests"] @@ -203,7 +228,7 @@ known-first-party = ["commitizen", "tests"] convention = "google" [tool.mypy] -files = "commitizen" +files = ["commitizen", "tests"] disallow_untyped_decorators = true disallow_subclassing_any = true warn_return_any = true @@ -226,19 +251,13 @@ poetry_command = "" [tool.poe.tasks] format.help = "Format the code" -format.sequence = [ - { cmd = "ruff check --fix commitizen tests" }, - { cmd = "ruff format commitizen tests" }, -] +format.sequence = [{ cmd = "ruff check --fix" }, { cmd = "ruff format" }] lint.help = "Lint the code" -lint.sequence = [ - { cmd = "ruff check commitizen/ tests/ --fix" }, - { cmd = "mypy commitizen/ tests/" }, -] +lint.sequence = [{ cmd = "ruff check" }, { cmd = "mypy" }] -check-commit.help = "Check the commit message" -check-commit.cmd = "cz -nr 3 check --rev-range origin/master.." +check-commit.help = "Check the commit messages" +check-commit.cmd = "poetry run cz --no-raise 3 check --rev-range origin/master.." test.help = "Run the test suite" test.cmd = "pytest -n 3 --dist=loadfile" @@ -250,14 +269,9 @@ cover.help = "Run the test suite with coverage" cover.ref = "test --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen" all.help = "Run all tasks" -all.sequence = [ - "format", - "lint", - "cover", - "check-commit", -] +all.sequence = ["format", "lint", "check-commit", "cover"] -"doc:screenshots".help = "Render documentation screeenshots" +"doc:screenshots".help = "Render documentation screenshots" "doc:screenshots".script = "scripts.gen_cli_help_screenshots:gen_cli_help_screenshots" "doc:build".help = "Build the documentation" @@ -267,10 +281,7 @@ doc.help = "Live documentation server" doc.cmd = "mkdocs serve" ci.help = "Run all tasks in CI" -ci.sequence = [ - { cmd = "pre-commit run --all-files" }, - "cover", -] +ci.sequence = ["check-commit", { cmd = "pre-commit run --all-files" }, "cover"] ci.env = { SKIP = "no-commit-to-branch" } setup-pre-commit.help = "Install pre-commit hooks" diff --git a/tests/commands/conftest.py b/tests/commands/conftest.py index 91931849b2..4f9b5de3c6 100644 --- a/tests/commands/conftest.py +++ b/tests/commands/conftest.py @@ -3,7 +3,8 @@ import pytest from commitizen import defaults -from commitizen.config import BaseConfig, JsonConfig +from commitizen.config import BaseConfig +from commitizen.config.json_config import JsonConfig @pytest.fixture() diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index b5ff7e6edb..8dbe8f447e 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -5,16 +5,14 @@ import sys from pathlib import Path from textwrap import dedent +from typing import TYPE_CHECKING from unittest.mock import MagicMock, call -import py import pytest -from pytest_mock import MockFixture import commitizen.commands.bump as bump -from commitizen import cli, cmd, git, hooks -from commitizen.changelog_formats import ChangelogFormat -from commitizen.cz.base import BaseCommitizen +from commitizen import cli, cmd, defaults, git, hooks +from commitizen.config.base_config import BaseConfig from commitizen.exceptions import ( BumpTagFailedError, CommitizenException, @@ -22,7 +20,6 @@ DryRunExit, ExitCode, ExpectedExit, - GetNextExit, InvalidManualVersion, NoCommitsFoundError, NoneIncrementExit, @@ -33,6 +30,13 @@ ) from tests.utils import create_file_and_commit, create_tag, skip_below_py_3_13 +if TYPE_CHECKING: + import py + from pytest_mock import MockFixture + + from commitizen.changelog_formats import ChangelogFormat + from commitizen.cz.base import BaseCommitizen + @pytest.mark.parametrize( "commit_msg", @@ -41,8 +45,8 @@ "fix(user): username exception", "refactor: remove ini configuration support", "refactor(config): remove ini configuration support", - "perf: update to use multiproess", - "perf(worker): update to use multiproess", + "perf: update to use multiprocess", + "perf(worker): update to use multiprocess", ), ) @pytest.mark.usefixtures("tmp_commitizen_project") @@ -209,7 +213,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 +285,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 +321,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 +441,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" ) @@ -646,13 +650,11 @@ def test_none_increment_should_not_call_git_tag_and_error_code_is_not_zero( dummy_value = git.tag("0.0.2") git.tag = MagicMock(return_value=dummy_value) - with pytest.raises(NoneIncrementExit): - try: - cli.main() - except NoneIncrementExit as e: - git.tag.assert_not_called() - assert e.exit_code == ExitCode.NO_INCREMENT - raise e + with pytest.raises(NoneIncrementExit) as e: + cli.main() + + git.tag.assert_not_called() + assert e.value.exit_code == ExitCode.NO_INCREMENT # restore pop stashed git.tag = stashed_git_tag @@ -1062,7 +1064,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 +1103,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 +1147,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 +1287,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 +1329,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, @@ -1455,7 +1457,7 @@ def test_bump_get_next(mocker: MockFixture, capsys): testargs = ["cz", "bump", "--yes", "--get-next"] mocker.patch.object(sys, "argv", testargs) - with pytest.raises(GetNextExit): + with pytest.raises(DryRunExit): cli.main() out, _ = capsys.readouterr() @@ -1475,7 +1477,7 @@ def test_bump_get_next_update_changelog_on_bump( testargs = ["cz", "bump", "--yes", "--get-next"] mocker.patch.object(sys, "argv", testargs) - with pytest.raises(GetNextExit): + with pytest.raises(DryRunExit): cli.main() out, _ = capsys.readouterr() @@ -1485,39 +1487,6 @@ def test_bump_get_next_update_changelog_on_bump( assert tag_exists is False -@pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_get_next__changelog_is_not_allowed(mocker: MockFixture): - create_file_and_commit("feat: new file") - - testargs = ["cz", "bump", "--yes", "--get-next", "--changelog"] - mocker.patch.object(sys, "argv", testargs) - - with pytest.raises(NotAllowed): - cli.main() - - -@pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_get_next__changelog_to_stdout_is_not_allowed(mocker: MockFixture): - create_file_and_commit("feat: new file") - - testargs = ["cz", "bump", "--yes", "--get-next", "--changelog-to-stdout"] - mocker.patch.object(sys, "argv", testargs) - - with pytest.raises(NotAllowed): - cli.main() - - -@pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_get_next__manual_version_is_not_allowed(mocker: MockFixture): - create_file_and_commit("feat: new file") - - testargs = ["cz", "bump", "--yes", "--get-next", "0.2.1"] - mocker.patch.object(sys, "argv", testargs) - - with pytest.raises(NotAllowed): - cli.main() - - @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_get_next__no_eligible_commits_raises(mocker: MockFixture): create_file_and_commit("chore: new commit") @@ -1656,3 +1625,83 @@ 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") + + +def test_is_initial_tag(mocker: MockFixture, tmp_commitizen_project): + """Test the _is_initial_tag method behavior.""" + # Create a commit but no tags + create_file_and_commit("feat: initial commit") + + # Initialize Bump with minimal config + config = BaseConfig() + config.settings.update( + { + "name": defaults.DEFAULT_SETTINGS["name"], + "encoding": "utf-8", + "pre_bump_hooks": [], + "post_bump_hooks": [], + } + ) + + # Initialize with required arguments + arguments = { + "changelog": False, + "changelog_to_stdout": False, + "git_output_to_stderr": False, + "no_verify": False, + "check_consistency": False, + "retry": False, + "version_scheme": None, + "file_name": None, + "template": None, + "extras": None, + } + + bump_cmd = bump.Bump(config, arguments) # type: ignore[arg-type] + + # Test case 1: No current tag, not yes mode + mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: True)) + assert bump_cmd._is_initial_tag(None, is_yes=False) is True + + # Test case 2: No current tag, yes mode + assert bump_cmd._is_initial_tag(None, is_yes=True) is True + + # Test case 3: Has current tag + mock_tag = mocker.Mock() + assert bump_cmd._is_initial_tag(mock_tag, is_yes=False) is False + + # Test case 4: No current tag, user denies + mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: False)) + assert bump_cmd._is_initial_tag(None, is_yes=False) is False diff --git a/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt b/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt index 5d4438875d..4cf8e6c91b 100644 --- a/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt +++ b/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt @@ -74,7 +74,7 @@ options: --version-scheme {pep440,semver,semver2} choose version scheme --version-type {pep440,semver,semver2} - Deprecated, use --version-scheme + Deprecated, use --version-scheme instead --build-metadata BUILD_METADATA Add additional build-metadata to the version-number --get-next Determine the next version and write to stdout diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py index f794b8d9f3..f2e2ecbd38 100644 --- a/tests/commands/test_changelog_command.py +++ b/tests/commands/test_changelog_command.py @@ -292,10 +292,11 @@ def test_changelog_hook(mocker: MockFixture, config: BaseConfig, dry_run: bool): config, {"unreleased_version": None, "incremental": True, "dry_run": dry_run} ) mocker.patch.object(changelog.cz, "changelog_hook", changelog_hook_mock) - try: + if dry_run: + with pytest.raises(DryRunExit): + changelog() + else: changelog() - except DryRunExit: - pass full_changelog = ( "## Unreleased\n\n### Refactor\n\n- is in changelog\n\n### Feat\n\n- new file\n" @@ -884,7 +885,6 @@ def test_changelog_with_filename_as_empty_string( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_first_version_from_arg( mocker: MockFixture, config_path, changelog_path, file_regression ): @@ -918,7 +918,6 @@ def test_changelog_from_rev_first_version_from_arg( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_latest_version_from_arg( mocker: MockFixture, config_path, changelog_path, file_regression ): @@ -954,7 +953,6 @@ def test_changelog_from_rev_latest_version_from_arg( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") @pytest.mark.parametrize( "rev_range,tag", ( @@ -986,7 +984,6 @@ def test_changelog_from_rev_range_not_found( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_multiple_matching_tags( mocker: MockFixture, config_path, changelog_path ): @@ -1016,7 +1013,6 @@ def test_changelog_multiple_matching_tags( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_range_default_tag_format( mocker, config_path, changelog_path ): @@ -1047,7 +1043,6 @@ def test_changelog_from_rev_range_default_tag_format( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_version_range_including_first_tag( mocker: MockFixture, config_path, changelog_path, file_regression ): @@ -1079,7 +1074,6 @@ def test_changelog_from_rev_version_range_including_first_tag( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_version_range_from_arg( mocker: MockFixture, config_path, changelog_path, file_regression ): @@ -1119,7 +1113,6 @@ def test_changelog_from_rev_version_range_from_arg( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_version_range_with_legacy_tags( mocker: MockFixture, config_path, changelog_path, file_regression ): @@ -1154,7 +1147,6 @@ def test_changelog_from_rev_version_range_with_legacy_tags( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_version_with_big_range_from_arg( mocker: MockFixture, config_path, changelog_path, file_regression ): @@ -1214,7 +1206,6 @@ def test_changelog_from_rev_version_with_big_range_from_arg( @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_latest_version_dry_run( mocker: MockFixture, capsys, config_path, changelog_path, file_regression ): @@ -1267,7 +1258,6 @@ def test_invalid_subject_is_skipped(mocker: MockFixture, capsys): assert out == ("## Unreleased\n\n### Feat\n\n- a new world\n\n") -@pytest.mark.freeze_time("2022-02-13") @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_with_customized_change_type_order( mocker, config_path, changelog_path, file_regression @@ -1325,7 +1315,6 @@ def test_empty_commit_list(mocker): @pytest.mark.usefixtures("tmp_commitizen_project") -@pytest.mark.freeze_time("2022-02-13") def test_changelog_prerelease_rev_with_use_scheme_semver( mocker: MockFixture, capsys, config_path, changelog_path, file_regression ): @@ -1481,7 +1470,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 +1512,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 +1777,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( @@ -1914,6 +1903,32 @@ def test_export_changelog_template_from_plugin( assert target.read_text() == tpl +def test_export_changelog_template_fails_when_template_has_no_filename( + mocker: MockFixture, + tmp_commitizen_project: Path, +): + project_root = Path(tmp_commitizen_project) + target = project_root / "changelog.jinja" + + # Mock a template object with no filename + class FakeTemplate: + filename = None + + # Patch get_changelog_template to return a template without a filename + mocker.patch( + "commitizen.changelog.get_changelog_template", return_value=FakeTemplate() + ) + + args = ["cz", "changelog", "--export-template", str(target)] + mocker.patch.object(sys, "argv", args) + + with pytest.raises(NotAllowed) as exc_info: + cli.main() + + assert not target.exists() + assert "Template filename is not set" in str(exc_info.value) + + @skip_below_py_3_13 def test_changelog_command_shows_description_when_use_help_option( mocker: MockFixture, capsys, file_regression 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..e7918ca238 100644 --- a/tests/commands/test_check_command.py +++ b/tests/commands/test_check_command.py @@ -2,18 +2,22 @@ import sys from io import StringIO +from typing import TYPE_CHECKING import pytest -from pytest_mock import MockFixture from commitizen import cli, commands, git from commitizen.exceptions import ( + CommitMessageLengthExceededError, InvalidCommandArgumentError, InvalidCommitMessageError, NoCommitsFoundError, ) from tests.utils import create_file_and_commit, skip_below_py_3_13 +if TYPE_CHECKING: + from pytest_mock import MockFixture + COMMIT_LOG = [ "refactor: A code change that neither fixes a bug nor adds a feature", r"refactor(cz/connventional_commit): use \S to check scope", @@ -365,7 +369,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( @@ -449,6 +453,70 @@ def test_check_command_with_message_length_limit_exceeded(config, mocker: MockFi arguments={"message": message, "message_length_limit": len(message) - 1}, ) - with pytest.raises(InvalidCommitMessageError): + with pytest.raises(CommitMessageLengthExceededError): check_cmd() error_mock.assert_called_once() + + +def test_check_command_with_amend_prefix_default(config, mocker: MockFixture): + success_mock = mocker.patch("commitizen.out.success") + check_cmd = commands.Check(config=config, arguments={"message": "amend! test"}) + + check_cmd() + success_mock.assert_called_once() + + +def test_check_command_with_config_message_length_limit(config, mocker: MockFixture): + success_mock = mocker.patch("commitizen.out.success") + message = "fix(scope): some commit message" + + config.settings["message_length_limit"] = len(message) + 1 + + check_cmd = commands.Check( + config=config, + arguments={"message": message}, + ) + + check_cmd() + success_mock.assert_called_once() + + +def test_check_command_with_config_message_length_limit_exceeded( + config, mocker: MockFixture +): + error_mock = mocker.patch("commitizen.out.error") + message = "fix(scope): some commit message" + + config.settings["message_length_limit"] = len(message) - 1 + + check_cmd = commands.Check( + config=config, + arguments={"message": message}, + ) + + with pytest.raises(CommitMessageLengthExceededError): + check_cmd() + error_mock.assert_called_once() + + +def test_check_command_cli_overrides_config_message_length_limit( + config, mocker: MockFixture +): + success_mock = mocker.patch("commitizen.out.success") + message = "fix(scope): some commit message" + + config.settings["message_length_limit"] = len(message) - 1 + + check_cmd = commands.Check( + config=config, + arguments={"message": message, "message_length_limit": len(message) + 1}, + ) + + check_cmd() + success_mock.assert_called_once() + + success_mock.reset_mock() + check_cmd = commands.Check( + config=config, + arguments={"message": message, "message_length_limit": None}, + ) diff --git a/tests/commands/test_check_command/test_check_command_shows_description_when_use_help_option.txt b/tests/commands/test_check_command/test_check_command_shows_description_when_use_help_option.txt index 85f42f6d2a..4066748557 100644 --- a/tests/commands/test_check_command/test_check_command_shows_description_when_use_help_option.txt +++ b/tests/commands/test_check_command/test_check_command_shows_description_when_use_help_option.txt @@ -1,5 +1,5 @@ usage: cz check [-h] [--commit-msg-file COMMIT_MSG_FILE | - --rev-range REV_RANGE | -m MESSAGE] [--allow-abort] + --rev-range REV_RANGE | -d | -m MESSAGE] [--allow-abort] [--allowed-prefixes [ALLOWED_PREFIXES ...]] [-l MESSAGE_LENGTH_LIMIT] @@ -13,6 +13,9 @@ options: MSG_FILE=$1 --rev-range REV_RANGE a range of git rev to check. e.g, master..HEAD + -d, --use-default-range + check from the default branch to HEAD. e.g, + refs/remotes/origin/master..HEAD -m, --message MESSAGE commit message that needs to be checked --allow-abort allow empty commit messages, which typically abort a diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 3a92f5af48..3e408576fe 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -73,7 +73,7 @@ def test_commit_backup_on_failure(config, mocker: MockFixture): with pytest.raises(CommitError): commit_cmd = commands.Commit(config, {}) - temp_file = commit_cmd.temp_file + temp_file = commit_cmd.backup_file_path commit_cmd() prompt_mock.assert_called_once() @@ -101,7 +101,7 @@ def test_commit_retry_works(config, mocker: MockFixture): success_mock = mocker.patch("commitizen.out.success") commit_cmd = commands.Commit(config, {"retry": True}) - temp_file = commit_cmd.temp_file + temp_file = commit_cmd.backup_file_path commit_cmd() commit_mock.assert_called_with("backup commit", args="") @@ -144,7 +144,7 @@ def test_commit_retry_after_failure_works(config, mocker: MockFixture): config.settings["retry_after_failure"] = True commit_cmd = commands.Commit(config, {}) - temp_file = commit_cmd.temp_file + temp_file = commit_cmd.backup_file_path commit_cmd() commit_mock.assert_called_with("backup commit", args="") @@ -171,7 +171,7 @@ def test_commit_retry_after_failure_with_no_retry_works(config, mocker: MockFixt config.settings["retry_after_failure"] = True commit_cmd = commands.Commit(config, {"no_retry": True}) - temp_file = commit_cmd.temp_file + temp_file = commit_cmd.backup_file_path commit_cmd() commit_mock.assert_called_with("feat: user created\n\ncloses #21", args="") @@ -523,3 +523,93 @@ def test_commit_command_shows_description_when_use_help_option( out, _ = capsys.readouterr() file_regression.check(out, extension=".txt") + + +@pytest.mark.usefixtures("staging_is_clean") +@pytest.mark.parametrize( + "out", ["no changes added to commit", "nothing added to commit"] +) +def test_commit_when_nothing_added_to_commit(config, mocker: MockFixture, out): + 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( + out=out, + err="", + stdout=out.encode(), + stderr=b"", + return_code=0, + ) + error_mock = mocker.patch("commitizen.out.error") + + commands.Commit(config, {})() + + commit_mock.assert_called_once() + error_mock.assert_called_once_with(out) + + +@pytest.mark.usefixtures("staging_is_clean") +def test_commit_command_with_config_message_length_limit(config, mocker: MockFixture): + prompt_mock = mocker.patch("questionary.prompt") + prefix = "feat" + subject = "random subject" + message_length = len(prefix) + len(": ") + len(subject) + prompt_mock.return_value = { + "prefix": prefix, + "subject": subject, + "scope": "", + "is_breaking_change": False, + "body": "random body", + "footer": "random 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") + + config.settings["message_length_limit"] = message_length + commands.Commit(config, {})() + success_mock.assert_called_once() + + config.settings["message_length_limit"] = message_length - 1 + with pytest.raises(CommitMessageLengthExceededError): + commands.Commit(config, {})() + + +@pytest.mark.usefixtures("staging_is_clean") +def test_commit_command_cli_overrides_config_message_length_limit( + config, mocker: MockFixture +): + prompt_mock = mocker.patch("questionary.prompt") + prefix = "feat" + subject = "random subject" + message_length = len(prefix) + len(": ") + len(subject) + prompt_mock.return_value = { + "prefix": prefix, + "subject": subject, + "scope": "", + "is_breaking_change": False, + "body": "random body", + "footer": "random 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") + + config.settings["message_length_limit"] = message_length - 1 + + commands.Commit(config, {"message_length_limit": message_length})() + success_mock.assert_called_once() + + success_mock.reset_mock() + commands.Commit(config, {"message_length_limit": None})() + success_mock.assert_called_once() diff --git a/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt b/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt index dd1f53f3da..ba531042aa 100644 --- a/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt +++ b/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt @@ -12,7 +12,7 @@ options: --write-message-to-file FILE_PATH write message to file before committing (can be combined with --dry-run) - -s, --signoff sign off the commit + -s, --signoff Deprecated, use 'cz commit -- -s' instead -a, --all Tell the command to automatically stage files that have been modified and deleted, but new files you have not told Git about are not affected. diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index ea18e89a2b..54fa271fd9 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -3,17 +3,22 @@ import json import os import sys -from typing import Any +from pathlib import Path +from typing import TYPE_CHECKING, Any import pytest import yaml -from pytest_mock import MockFixture -from commitizen import cli, commands +from commitizen import cli, cmd, commands from commitizen.__version__ import __version__ from commitizen.exceptions import InitFailedError, NoAnswersError from tests.utils import skip_below_py_3_10 +if TYPE_CHECKING: + from pytest_mock import MockFixture + + from commitizen.config.base_config import BaseConfig + class FakeQuestion: def __init__(self, expected_return): @@ -32,7 +37,7 @@ def unsafe_ask(self): "rev": f"v{__version__}", "hooks": [ {"id": "commitizen"}, - {"id": "commitizen-branch", "stages": ["push"]}, + {"id": "commitizen-branch", "stages": ["pre-push"]}, ], } @@ -58,7 +63,9 @@ def unsafe_ask(self): } -def test_init_without_setup_pre_commit_hook(tmpdir, mocker: MockFixture, config): +def test_init_without_setup_pre_commit_hook( + tmpdir, mocker: MockFixture, config: BaseConfig +): mocker.patch( "questionary.select", side_effect=[ @@ -72,7 +79,6 @@ def test_init_without_setup_pre_commit_hook(tmpdir, mocker: MockFixture, config) mocker.patch("questionary.text", return_value=FakeQuestion("$version")) # Return None to skip hook installation mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) - with tmpdir.as_cwd(): commands.Init(config)() @@ -83,17 +89,17 @@ def test_init_without_setup_pre_commit_hook(tmpdir, mocker: MockFixture, config) assert not os.path.isfile(pre_commit_config_filename) -def test_init_when_config_already_exists(config, capsys): +def test_init_when_config_already_exists(config: BaseConfig, capsys): # Set config path - path = os.sep.join(["tests", "pyproject.toml"]) - config.add_path(path) + path = Path(os.sep.join(["tests", "pyproject.toml"])) + config.path = path commands.Init(config)() captured = capsys.readouterr() assert captured.out == f"Config file {path} already exists\n" -def test_init_without_choosing_tag(config, mocker: MockFixture, tmpdir): +def test_init_without_choosing_tag(config: BaseConfig, mocker: MockFixture, tmpdir): mocker.patch( "commitizen.commands.init.get_tag_names", return_value=["0.0.2", "0.0.1"] ) @@ -115,23 +121,17 @@ def test_init_without_choosing_tag(config, mocker: MockFixture, tmpdir): commands.Init(config)() -def test_executed_pre_commit_command(config): - init = commands.Init(config) - expected_cmd = "pre-commit install --hook-type commit-msg --hook-type pre-push" - assert init._gen_pre_commit_cmd(["commit-msg", "pre-push"]) == expected_cmd - - @pytest.fixture(scope="function") def pre_commit_installed(mocker: MockFixture): # Assume the `pre-commit` is installed mocker.patch( - "commitizen.commands.init.ProjectInfo.is_pre_commit_installed", + "commitizen.project_info.is_pre_commit_installed", return_value=True, ) # And installation success (i.e. no exception raised) mocker.patch( - "commitizen.commands.init.Init._exec_install_pre_commit_hook", - return_value=None, + "commitizen.cmd.run", + return_value=cmd.Command("0.0.1", "", b"", b"", 0), ) @@ -155,17 +155,14 @@ def default_choice(request, mocker: MockFixture): yield request.param -def check_cz_config(config: str): +def check_cz_config(config_filepath: str): """ Check the content of commitizen config is as expected - - Args: - config: The config path """ - with open(config) as file: - if "json" in config: + with open(config_filepath) as file: + if "json" in config_filepath: assert json.load(file) == EXPECTED_DICT_CONFIG - elif "yaml" in config: + elif "yaml" in config_filepath: assert yaml.load(file, Loader=yaml.FullLoader) == EXPECTED_DICT_CONFIG else: config_data = file.read() @@ -183,13 +180,17 @@ 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: str, tmpdir, config: BaseConfig + ): with tmpdir.as_cwd(): commands.Init(config)() check_cz_config(default_choice) check_pre_commit_config([cz_hook_config]) - def test_empty_pre_commit_config(_, default_choice, tmpdir, config): + def test_empty_pre_commit_config( + _, default_choice: str, tmpdir, config: BaseConfig + ): with tmpdir.as_cwd(): p = tmpdir.join(pre_commit_config_filename) p.write("") @@ -198,7 +199,9 @@ def test_empty_pre_commit_config(_, default_choice, tmpdir, config): check_cz_config(default_choice) check_pre_commit_config([cz_hook_config]) - def test_pre_commit_config_without_cz_hook(_, default_choice, tmpdir, config): + def test_pre_commit_config_without_cz_hook( + _, default_choice: str, tmpdir, config: BaseConfig + ): existing_hook_config = { "repo": "https://github.com/pre-commit/pre-commit-hooks", "rev": "v1.2.3", @@ -213,7 +216,9 @@ def test_pre_commit_config_without_cz_hook(_, default_choice, tmpdir, config): check_cz_config(default_choice) check_pre_commit_config([existing_hook_config, cz_hook_config]) - def test_cz_hook_exists_in_pre_commit_config(_, default_choice, tmpdir, config): + def test_cz_hook_exists_in_pre_commit_config( + _, default_choice: str, tmpdir, config: BaseConfig + ): with tmpdir.as_cwd(): p = tmpdir.join(pre_commit_config_filename) p.write(yaml.safe_dump({"repos": [cz_hook_config]})) @@ -226,33 +231,48 @@ def test_cz_hook_exists_in_pre_commit_config(_, default_choice, tmpdir, config): class TestNoPreCommitInstalled: def test_pre_commit_not_installed( - _, mocker: MockFixture, config, default_choice, tmpdir + _, mocker: MockFixture, config: BaseConfig, default_choice: str, tmpdir ): # Assume `pre-commit` is not installed mocker.patch( - "commitizen.commands.init.ProjectInfo.is_pre_commit_installed", + "commitizen.project_info.is_pre_commit_installed", return_value=False, ) with tmpdir.as_cwd(): with pytest.raises(InitFailedError): commands.Init(config)() - def test_pre_commit_exec_failed( - _, mocker: MockFixture, config, default_choice, tmpdir - ): - # Assume `pre-commit` is installed - mocker.patch( - "commitizen.commands.init.ProjectInfo.is_pre_commit_installed", - return_value=True, - ) - # But pre-commit installation will fail - mocker.patch( - "commitizen.commands.init.Init._exec_install_pre_commit_hook", - side_effect=InitFailedError("Mock init failed error."), - ) - with tmpdir.as_cwd(): - with pytest.raises(InitFailedError): - commands.Init(config)() + +class TestAskTagFormat: + def test_confirm_v_tag_format(self, mocker: MockFixture, config: BaseConfig): + init = commands.Init(config) + mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) + + result = init._ask_tag_format("v1.0.0") + assert result == r"v$version" + + def test_reject_v_tag_format(self, mocker: MockFixture, config: BaseConfig): + init = commands.Init(config) + mocker.patch("questionary.confirm", return_value=FakeQuestion(False)) + mocker.patch("questionary.text", return_value=FakeQuestion("custom-$version")) + + result = init._ask_tag_format("v1.0.0") + assert result == "custom-$version" + + def test_non_v_tag_format(self, mocker: MockFixture, config: BaseConfig): + init = commands.Init(config) + mocker.patch("questionary.text", return_value=FakeQuestion("custom-$version")) + + result = init._ask_tag_format("1.0.0") + assert result == "custom-$version" + + def test_empty_input_returns_default(self, mocker: MockFixture, config: BaseConfig): + init = commands.Init(config) + mocker.patch("questionary.confirm", return_value=FakeQuestion(False)) + mocker.patch("questionary.text", return_value=FakeQuestion("")) + + result = init._ask_tag_format("v1.0.0") + assert result == "$version" # This is the default format from DEFAULT_SETTINGS @skip_below_py_3_10 @@ -266,3 +286,194 @@ def test_init_command_shows_description_when_use_help_option( out, _ = capsys.readouterr() file_regression.check(out, extension=".txt") + + +def test_init_with_confirmed_tag_format( + config: BaseConfig, mocker: MockFixture, tmpdir +): + mocker.patch( + "commitizen.commands.init.get_tag_names", return_value=["v0.0.2", "v0.0.1"] + ) + mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value="v0.0.2") + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion("pyproject.toml"), + FakeQuestion("cz_conventional_commits"), + FakeQuestion("commitizen"), + FakeQuestion("semver"), + ], + ) + mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) + + with tmpdir.as_cwd(): + commands.Init(config)() + with open("pyproject.toml", encoding="utf-8") as toml_file: + assert 'tag_format = "v$version"' in toml_file.read() + + +def test_init_with_no_existing_tags(config: BaseConfig, mocker: MockFixture, tmpdir): + mocker.patch("commitizen.commands.init.get_tag_names", return_value=[]) + mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value="v1.0.0") + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion("pyproject.toml"), + FakeQuestion("cz_conventional_commits"), + FakeQuestion("commitizen"), + FakeQuestion("semver"), + ], + ) + mocker.patch("questionary.confirm", return_value=FakeQuestion(False)) + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) + + with tmpdir.as_cwd(): + commands.Init(config)() + with open("pyproject.toml", encoding="utf-8") as toml_file: + assert 'version = "0.0.1"' in toml_file.read() + + +def test_init_with_no_existing_latest_tag( + config: BaseConfig, mocker: MockFixture, tmpdir +): + mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value=None) + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion("pyproject.toml"), + FakeQuestion("cz_conventional_commits"), + FakeQuestion("commitizen"), + FakeQuestion("semver"), + ], + ) + mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) + + with tmpdir.as_cwd(): + commands.Init(config)() + with open("pyproject.toml", encoding="utf-8") as toml_file: + assert 'version = "0.0.1"' in toml_file.read() + + +def test_init_with_existing_tags(config: BaseConfig, mocker: MockFixture, tmpdir): + expected_tags = ["v1.0.0", "v0.9.0", "v0.8.0"] + mocker.patch("commitizen.commands.init.get_tag_names", return_value=expected_tags) + mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value="v1.0.0") + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion("pyproject.toml"), + FakeQuestion("cz_conventional_commits"), + FakeQuestion("commitizen"), + FakeQuestion("semver"), # Select version scheme first + FakeQuestion("v1.0.0"), # Then select the latest tag + ], + ) + mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) + + with tmpdir.as_cwd(): + commands.Init(config)() + with open("pyproject.toml", encoding="utf-8") as toml_file: + assert 'version = "1.0.0"' in toml_file.read() + + +def test_init_with_valid_tag_selection(config: BaseConfig, mocker: MockFixture, tmpdir): + expected_tags = ["v1.0.0", "v0.9.0", "v0.8.0"] + mocker.patch("commitizen.commands.init.get_tag_names", return_value=expected_tags) + mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value="v1.0.0") + + # Mock all questionary.select calls in the exact order they appear in Init.__call__ + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion("pyproject.toml"), # _ask_config_path + FakeQuestion("cz_conventional_commits"), # _ask_name + FakeQuestion("commitizen"), # _ask_version_provider + FakeQuestion("v0.9.0"), # _ask_tag (after confirm=False) + FakeQuestion("semver"), # _ask_version_scheme + ], + ) + + mocker.patch( + "questionary.confirm", return_value=FakeQuestion(False) + ) # Don't confirm latest tag + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) + + with tmpdir.as_cwd(): + commands.Init(config)() + with open("pyproject.toml", encoding="utf-8") as toml_file: + content = toml_file.read() + assert 'version = "0.9.0"' in content + assert 'version_scheme = "semver"' in content + + +def test_init_configuration_settings(tmpdir, mocker: MockFixture, config: BaseConfig): + """Test that all configuration settings are properly initialized.""" + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion("pyproject.toml"), + FakeQuestion("cz_conventional_commits"), + FakeQuestion("commitizen"), + FakeQuestion("semver"), + ], + ) + mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) + + with tmpdir.as_cwd(): + commands.Init(config)() + + with open("pyproject.toml", encoding="utf-8") as toml_file: + config_data = toml_file.read() + + # Verify all expected settings are present + assert 'name = "cz_conventional_commits"' in config_data + assert 'tag_format = "$version"' in config_data + assert 'version_scheme = "semver"' in config_data + assert 'version = "0.0.1"' in config_data + assert "update_changelog_on_bump = true" in config_data + assert "major_version_zero = true" in config_data + + +def test_init_configuration_with_version_provider( + tmpdir, mocker: MockFixture, config: BaseConfig +): + """Test configuration initialization with a different version provider.""" + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion("pyproject.toml"), + FakeQuestion("cz_conventional_commits"), + FakeQuestion("pep621"), # Different version provider + FakeQuestion("semver"), + ], + ) + mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) + + with tmpdir.as_cwd(): + commands.Init(config)() + + with open("pyproject.toml", encoding="utf-8") as toml_file: + config_data = toml_file.read() + + # Verify version provider is set instead of version + assert 'name = "cz_conventional_commits"' in config_data + assert 'tag_format = "$version"' in config_data + assert 'version_scheme = "semver"' in config_data + assert 'version_provider = "pep621"' in config_data + assert "update_changelog_on_bump = true" in config_data + assert "major_version_zero = true" in config_data + assert ( + "version = " not in config_data + ) # Version should not be set when using version_provider diff --git a/tests/commands/test_version_command.py b/tests/commands/test_version_command.py index 927cf55f25..a5faf4e16d 100644 --- a/tests/commands/test_version_command.py +++ b/tests/commands/test_version_command.py @@ -1,3 +1,4 @@ +import os import platform import sys @@ -10,54 +11,51 @@ from tests.utils import skip_below_py_3_10 -def test_version_for_showing_project_version(config, capsys): - # No version exist +def test_version_for_showing_project_version_error(config, capsys): + # No version specified in config commands.Version( config, - {"report": False, "project": True, "commitizen": False, "verbose": False}, + {"project": True}, )() captured = capsys.readouterr() assert "No project information in this project." in captured.err + +def test_version_for_showing_project_version(config, capsys): config.settings["version"] = "v0.0.1" commands.Version( config, - {"report": False, "project": True, "commitizen": False, "verbose": False}, + {"project": True}, )() captured = capsys.readouterr() assert "v0.0.1" in captured.out -def test_version_for_showing_commitizen_version(config, capsys): - commands.Version( - config, - {"report": False, "project": False, "commitizen": True, "verbose": False}, - )() - captured = capsys.readouterr() - assert f"{__version__}" in captured.out - - # default showing commitizen version +@pytest.mark.parametrize("project", (True, False)) +def test_version_for_showing_commitizen_version(config, capsys, project: bool): commands.Version( config, - {"report": False, "project": False, "commitizen": False, "verbose": False}, + {"project": project, "commitizen": True}, )() captured = capsys.readouterr() assert f"{__version__}" in captured.out -def test_version_for_showing_both_versions(config, capsys): +def test_version_for_showing_both_versions_no_project(config, capsys): commands.Version( config, - {"report": False, "project": False, "commitizen": False, "verbose": True}, + {"verbose": True}, )() captured = capsys.readouterr() assert f"Installed Commitizen Version: {__version__}" in captured.out assert "No project information in this project." in captured.err + +def test_version_for_showing_both_versions(config, capsys): config.settings["version"] = "v0.0.1" commands.Version( config, - {"report": False, "project": False, "commitizen": False, "verbose": True}, + {"verbose": True}, )() captured = capsys.readouterr() expected_out = ( @@ -69,7 +67,7 @@ def test_version_for_showing_both_versions(config, capsys): def test_version_for_showing_commitizen_system_info(config, capsys): commands.Version( config, - {"report": True, "project": False, "commitizen": False, "verbose": False}, + {"report": True}, )() captured = capsys.readouterr() assert f"Commitizen Version: {__version__}" in captured.out @@ -95,9 +93,7 @@ def test_version_use_version_provider( commands.Version( config, { - "report": False, "project": project, - "commitizen": False, "verbose": not project, }, )() @@ -113,10 +109,82 @@ def test_version_use_version_provider( def test_version_command_shows_description_when_use_help_option( mocker: MockerFixture, capsys, file_regression ): - testargs = ["cz", "version", "--help"] - mocker.patch.object(sys, "argv", testargs) - with pytest.raises(SystemExit): - cli.main() + # Force consistent terminal width for tests to avoid wrapping differences + # between single and multi-worker pytest modes + original_columns = os.environ.get("COLUMNS") + os.environ["COLUMNS"] = "80" + + try: + testargs = ["cz", "version", "--help"] + mocker.patch.object(sys, "argv", testargs) + with pytest.raises(SystemExit): + cli.main() + + out, _ = capsys.readouterr() + file_regression.check(out, extension=".txt") + finally: + # Restore original COLUMNS + if original_columns is not None: + os.environ["COLUMNS"] = original_columns + else: + os.environ.pop("COLUMNS", None) + + +@pytest.mark.parametrize( + "version, expected_version", + [ + ("1.0.0", "1\n"), + ("2.1.3", "2\n"), + ("0.0.1", "0\n"), + ("0.1.0", "0\n"), + ], +) +def test_version_just_major(config, capsys, version: str, expected_version: str): + config.settings["version"] = version + commands.Version( + config, + { + "project": True, + "major": True, + }, + )() + captured = capsys.readouterr() + assert expected_version == captured.out + + +@pytest.mark.parametrize( + "version, expected_version", + [ + ("1.0.0", "0\n"), + ("2.1.3", "1\n"), + ("0.0.1", "0\n"), + ("0.1.0", "1\n"), + ], +) +def test_version_just_minor(config, capsys, version: str, expected_version: str): + config.settings["version"] = version + commands.Version( + config, + { + "project": True, + "minor": True, + }, + )() + captured = capsys.readouterr() + assert expected_version == captured.out - out, _ = capsys.readouterr() - file_regression.check(out, extension=".txt") + +@pytest.mark.parametrize("argument", ("major", "minor")) +def test_version_just_major_error_no_project(config, capsys, argument: str): + commands.Version( + config, + { + argument: True, # type: ignore[misc] + }, + )() + captured = capsys.readouterr() + assert not captured.out + assert ( + "Major or minor version can only be used with --project or --verbose." + in captured.err + ) diff --git a/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt b/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt index c461b10bcd..a194615a98 100644 --- a/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt +++ b/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt @@ -1,4 +1,4 @@ -usage: cz version [-h] [-r | -p | -c | -v] +usage: cz version [-h] [-r | -p | -c | -v] [--major | --minor] get the version of the installed commitizen or the current project (default: installed commitizen) @@ -10,3 +10,7 @@ options: -c, --commitizen get the version of the installed commitizen -v, --verbose get the version of both the installed commitizen and the current project + --major get just the major version. Need to be used with --project + or --verbose. + --minor get just the minor version. Need to be used with --project + or --verbose. diff --git a/tests/conftest.py b/tests/conftest.py index 3d88f19b12..5eaa17440a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,11 +3,9 @@ import os import re import tempfile -from collections.abc import Iterator -from pathlib import Path +from typing import TYPE_CHECKING import pytest -from pytest_mock import MockerFixture from commitizen import cmd, defaults from commitizen.changelog_formats import ( @@ -17,6 +15,14 @@ from commitizen.config import BaseConfig from commitizen.cz import registry from commitizen.cz.base import BaseCommitizen + +if TYPE_CHECKING: + from collections.abc import Iterator, Mapping + from pathlib import Path + + from pytest_mock import MockerFixture + + from commitizen.question import CzQuestion from tests.utils import create_file_and_commit SIGNER = "GitHub Action" @@ -169,14 +175,14 @@ class SemverCommitizen(BaseCommitizen): "patch": "PATCH", } changelog_pattern = r"^(patch|minor|major)" - commit_parser = r"^(?P<change_type>patch|minor|major)(?:\((?P<scope>[^()\r\n]*)\)|\()?:?\s(?P<message>.+)" # noqa + commit_parser = r"^(?P<change_type>patch|minor|major)(?:\((?P<scope>[^()\r\n]*)\)|\()?:?\s(?P<message>.+)" change_type_map = { "major": "Breaking Changes", "minor": "Features", "patch": "Bugs", } - def questions(self) -> list: + def questions(self) -> list[CzQuestion]: return [ { "type": "list", @@ -209,11 +215,23 @@ def questions(self) -> list: }, ] - def message(self, answers: dict) -> str: + def message(self, answers: Mapping) -> str: prefix = answers["prefix"] subject = answers.get("subject", "default message").trim() return f"{prefix}: {subject}" + def example(self) -> str: + return "" + + def schema(self) -> str: + return "" + + def schema_pattern(self) -> str: + return "" + + def info(self) -> str: + return "" + @pytest.fixture() def use_cz_semver(mocker): @@ -222,17 +240,29 @@ def use_cz_semver(mocker): class MockPlugin(BaseCommitizen): - def questions(self) -> defaults.Questions: + def questions(self) -> list[CzQuestion]: return [] - def message(self, answers: dict) -> str: + def message(self, answers: Mapping) -> str: + return "" + + def example(self) -> str: + return "" + + def schema(self) -> str: + return "" + + def schema_pattern(self) -> str: + return "" + + def info(self) -> str: return "" @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/providers/conftest.py b/tests/providers/conftest.py index f73cdb72a5..41b7bd02f1 100644 --- a/tests/providers/conftest.py +++ b/tests/providers/conftest.py @@ -1,11 +1,14 @@ from __future__ import annotations import os -from collections.abc import Iterator from pathlib import Path +from typing import TYPE_CHECKING import pytest +if TYPE_CHECKING: + from collections.abc import Iterator + @pytest.fixture def chdir(tmp_path: Path) -> Iterator[Path]: diff --git a/tests/providers/test_base_provider.py b/tests/providers/test_base_provider.py index 482bd698ea..782a8ba89e 100644 --- a/tests/providers/test_base_provider.py +++ b/tests/providers/test_base_provider.py @@ -1,12 +1,16 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import pytest -from commitizen.config.base_config import BaseConfig from commitizen.exceptions import VersionProviderUnknown from commitizen.providers import get_provider from commitizen.providers.commitizen_provider import CommitizenProvider +if TYPE_CHECKING: + from commitizen.config.base_config import BaseConfig + def test_default_version_provider_is_commitizen_config(config: BaseConfig): provider = get_provider(config) diff --git a/tests/providers/test_cargo_provider.py b/tests/providers/test_cargo_provider.py index 646ef3a53d..ea15fdbf39 100644 --- a/tests/providers/test_cargo_provider.py +++ b/tests/providers/test_cargo_provider.py @@ -1,44 +1,238 @@ from __future__ import annotations +import os from pathlib import Path from textwrap import dedent +from typing import TYPE_CHECKING import pytest -from commitizen.config.base_config import BaseConfig from commitizen.providers import get_provider from commitizen.providers.cargo_provider import CargoProvider +if TYPE_CHECKING: + from commitizen.config.base_config import BaseConfig + CARGO_TOML = """\ [package] name = "whatever" version = "0.1.0" """ -CARGO_EXPECTED = """\ +CARGO_TOML_EXPECTED = """\ [package] name = "whatever" version = "42.1" """ CARGO_WORKSPACE_TOML = """\ +[workspace] +members = ["member1", "folder/member2", "crates/*"] +exclude = ["crates/member4", "folder/member5"] + [workspace.package] -name = "whatever" version = "0.1.0" """ -CARGO_WORKSPACE_EXPECTED = """\ +CARGO_WORKSPACE_MEMBERS = [ + { + "path": "member1", + "content": """\ +[package] +name = "member1" +version.workspace = true +""", + }, + { + "path": "folder/member2", + "content": """\ +[package] +name = "member2" +version.workspace = "1.1.1" +""", + }, + { + "path": "crates/member3", + "content": """\ +[package] +name = "member3" +version.workspace = true +""", + }, + { + "path": "crates/member4", + "content": """\ +[package] +name = "member4" +version.workspace = "2.2.2" +""", + }, + { + "path": "folder/member5", + "content": """\ +[package] +name = "member5" +version.workspace = "3.3.3" +""", + }, +] + + +CARGO_WORKSPACE_TOML_EXPECTED = """\ +[workspace] +members = ["member1", "folder/member2", "crates/*"] +exclude = ["crates/member4", "folder/member5"] + [workspace.package] +version = "42.1" +""" + +CARGO_LOCK = """\ +[[package]] +name = "whatever" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] +""" + +CARGO_LOCK_EXPECTED = """\ +[[package]] name = "whatever" version = "42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] +""" + +CARGO_WORKSPACE_LOCK = """\ +[[package]] +name = "member1" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member3" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member4" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member5" +version = "3.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] +""" + +CARGO_WORKSPACE_LOCK_EXPECTED = """\ +[[package]] +name = "member1" +version = "42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member3" +version = "42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member4" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] + +[[package]] +name = "member5" +version = "3.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +dependencies = [ + "packageA", + "packageB", + "packageC", +] """ @pytest.mark.parametrize( "content, expected", ( - (CARGO_TOML, CARGO_EXPECTED), - (CARGO_WORKSPACE_TOML, CARGO_WORKSPACE_EXPECTED), + (CARGO_TOML, CARGO_TOML_EXPECTED), + (CARGO_WORKSPACE_TOML, CARGO_WORKSPACE_TOML_EXPECTED), ), ) def test_cargo_provider( @@ -58,3 +252,202 @@ def test_cargo_provider( provider.set_version("42.1") assert file.read_text() == dedent(expected) + + +@pytest.mark.parametrize( + "toml_content, lock_content, toml_expected, lock_expected", + ( + ( + CARGO_TOML, + CARGO_LOCK, + CARGO_TOML_EXPECTED, + CARGO_LOCK_EXPECTED, + ), + ( + CARGO_WORKSPACE_TOML, + CARGO_WORKSPACE_LOCK, + CARGO_WORKSPACE_TOML_EXPECTED, + CARGO_WORKSPACE_LOCK_EXPECTED, + ), + ), +) +def test_cargo_provider_with_lock( + config: BaseConfig, + chdir: Path, + toml_content: str, + lock_content: str, + toml_expected: str, + lock_expected: str, +): + filename = CargoProvider.filename + file = chdir / filename + file.write_text(dedent(toml_content)) + + # Create workspace members + os.mkdir(chdir / "crates") + os.mkdir(chdir / "folder") + for i in range(0, 5): + member_folder = Path(CARGO_WORKSPACE_MEMBERS[i]["path"]) + os.mkdir(member_folder) + member_file = member_folder / "Cargo.toml" + member_file.write_text(dedent(CARGO_WORKSPACE_MEMBERS[i]["content"])) + + lock_filename = CargoProvider.lock_filename + lock_file = chdir / lock_filename + lock_file.write_text(dedent(lock_content)) + config.settings["version_provider"] = "cargo" + + provider = get_provider(config) + assert isinstance(provider, CargoProvider) + assert provider.get_version() == "0.1.0" + + provider.set_version("42.1") + assert file.read_text() == dedent(toml_expected) + assert lock_file.read_text() == dedent(lock_expected) + + +def test_cargo_provider_workspace_member_without_version_key( + config: BaseConfig, + chdir: Path, +): + """Test workspace member that has no version key at all (should not crash).""" + workspace_toml = """\ +[workspace] +members = ["member_without_version"] + +[workspace.package] +version = "0.1.0" +""" + + # Create a member that has no version key at all + member_content = """\ +[package] +name = "member_without_version" +# No version key - this should trigger NonExistentKey exception +""" + + lock_content = """\ +[[package]] +name = "member_without_version" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +""" + + expected_workspace_toml = """\ +[workspace] +members = ["member_without_version"] + +[workspace.package] +version = "42.1" +""" + + expected_lock_content = """\ +[[package]] +name = "member_without_version" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +""" + + # Create the workspace file + filename = CargoProvider.filename + file = chdir / filename + file.write_text(dedent(workspace_toml)) + + # Create the member directory and file + os.mkdir(chdir / "member_without_version") + member_file = chdir / "member_without_version" / "Cargo.toml" + member_file.write_text(dedent(member_content)) + + # Create the lock file + lock_filename = CargoProvider.lock_filename + lock_file = chdir / lock_filename + lock_file.write_text(dedent(lock_content)) + + config.settings["version_provider"] = "cargo" + + provider = get_provider(config) + assert isinstance(provider, CargoProvider) + assert provider.get_version() == "0.1.0" + + # This should not crash even though the member has no version key + provider.set_version("42.1") + assert file.read_text() == dedent(expected_workspace_toml) + # The lock file should remain unchanged since the member doesn't inherit workspace version + assert lock_file.read_text() == dedent(expected_lock_content) + + +def test_cargo_provider_workspace_member_without_workspace_key( + config: BaseConfig, + chdir: Path, +): + """Test workspace member that has version key but no workspace subkey.""" + workspace_toml = """\ +[workspace] +members = ["member_without_workspace"] + +[workspace.package] +version = "0.1.0" +""" + + # Create a member that has version as a table but no workspace subkey + # This should trigger NonExistentKey when trying to access version["workspace"] + member_content = """\ +[package] +name = "member_without_workspace" + +[package.version] +# Has version table but no workspace key - should trigger NonExistentKey +""" + + lock_content = """\ +[[package]] +name = "member_without_workspace" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +""" + + expected_workspace_toml = """\ +[workspace] +members = ["member_without_workspace"] + +[workspace.package] +version = "42.1" +""" + + expected_lock_content = """\ +[[package]] +name = "member_without_workspace" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123abc" +""" + + # Create the workspace file + filename = CargoProvider.filename + file = chdir / filename + file.write_text(dedent(workspace_toml)) + + # Create the member directory and file + os.mkdir(chdir / "member_without_workspace") + member_file = chdir / "member_without_workspace" / "Cargo.toml" + member_file.write_text(dedent(member_content)) + + # Create the lock file + lock_filename = CargoProvider.lock_filename + lock_file = chdir / lock_filename + lock_file.write_text(dedent(lock_content)) + + config.settings["version_provider"] = "cargo" + + provider = get_provider(config) + assert isinstance(provider, CargoProvider) + assert provider.get_version() == "0.1.0" + + # This should not crash even though the member has no version.workspace key + provider.set_version("42.1") + assert file.read_text() == dedent(expected_workspace_toml) + # The lock file should remain unchanged since the member doesn't inherit workspace version + assert lock_file.read_text() == dedent(expected_lock_content) diff --git a/tests/providers/test_commitizen_provider.py b/tests/providers/test_commitizen_provider.py index b8df60da93..c28dc90f79 100644 --- a/tests/providers/test_commitizen_provider.py +++ b/tests/providers/test_commitizen_provider.py @@ -2,12 +2,13 @@ from typing import TYPE_CHECKING -from commitizen.config.base_config import BaseConfig from commitizen.providers.commitizen_provider import CommitizenProvider if TYPE_CHECKING: from pytest_mock import MockerFixture + from commitizen.config.base_config import BaseConfig + def test_commitizen_provider(config: BaseConfig, mocker: MockerFixture): config.settings["version"] = "42" diff --git a/tests/providers/test_composer_provider.py b/tests/providers/test_composer_provider.py index 45cbc8afa4..22357b7a7f 100644 --- a/tests/providers/test_composer_provider.py +++ b/tests/providers/test_composer_provider.py @@ -1,14 +1,18 @@ from __future__ import annotations -from pathlib import Path from textwrap import dedent +from typing import TYPE_CHECKING import pytest -from commitizen.config.base_config import BaseConfig from commitizen.providers import get_provider from commitizen.providers.composer_provider import ComposerProvider +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig + COMPOSER_JSON = """\ { "name": "whatever", diff --git a/tests/providers/test_npm_provider.py b/tests/providers/test_npm_provider.py index bc9399916d..785a2cb7fd 100644 --- a/tests/providers/test_npm_provider.py +++ b/tests/providers/test_npm_provider.py @@ -1,14 +1,18 @@ from __future__ import annotations -from pathlib import Path from textwrap import dedent +from typing import TYPE_CHECKING import pytest -from commitizen.config.base_config import BaseConfig from commitizen.providers import get_provider from commitizen.providers.npm_provider import NpmProvider +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig + NPM_PACKAGE_JSON = """\ { "name": "whatever", diff --git a/tests/providers/test_pep621_provider.py b/tests/providers/test_pep621_provider.py index 16bb479cc4..f44cef38c8 100644 --- a/tests/providers/test_pep621_provider.py +++ b/tests/providers/test_pep621_provider.py @@ -1,14 +1,18 @@ from __future__ import annotations -from pathlib import Path from textwrap import dedent +from typing import TYPE_CHECKING import pytest -from commitizen.config.base_config import BaseConfig from commitizen.providers import get_provider from commitizen.providers.pep621_provider import Pep621Provider +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig + PEP621_TOML = """\ [project] version = "0.1.0" diff --git a/tests/providers/test_poetry_provider.py b/tests/providers/test_poetry_provider.py index e26e2a44fb..ad998d41c7 100644 --- a/tests/providers/test_poetry_provider.py +++ b/tests/providers/test_poetry_provider.py @@ -1,14 +1,18 @@ from __future__ import annotations -from pathlib import Path from textwrap import dedent +from typing import TYPE_CHECKING import pytest -from commitizen.config.base_config import BaseConfig from commitizen.providers import get_provider from commitizen.providers.poetry_provider import PoetryProvider +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig + POETRY_TOML = """\ [tool.poetry] version = "0.1.0" diff --git a/tests/providers/test_scm_provider.py b/tests/providers/test_scm_provider.py index 9d955b2323..e28a6782ab 100644 --- a/tests/providers/test_scm_provider.py +++ b/tests/providers/test_scm_provider.py @@ -1,8 +1,9 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import pytest -from commitizen.config.base_config import BaseConfig from commitizen.providers import get_provider from commitizen.providers.scm_provider import ScmProvider from tests.utils import ( @@ -13,6 +14,9 @@ switch_branch, ) +if TYPE_CHECKING: + from commitizen.config.base_config import BaseConfig + @pytest.mark.parametrize( "tag_format,tag,expected_version", diff --git a/tests/providers/test_uv_provider.py b/tests/providers/test_uv_provider.py index 4093709376..7cb0782dd6 100644 --- a/tests/providers/test_uv_provider.py +++ b/tests/providers/test_uv_provider.py @@ -2,13 +2,14 @@ from typing import TYPE_CHECKING -from commitizen.config.base_config import BaseConfig from commitizen.providers import get_provider from commitizen.providers.uv_provider import UvProvider if TYPE_CHECKING: from pytest_regressions.file_regression import FileRegressionFixture + from commitizen.config.base_config import BaseConfig + PYPROJECT_TOML = """ [project] diff --git a/tests/test_bump_find_increment.py b/tests/test_bump_find_increment.py index ff24ff17a7..77e11c78c7 100644 --- a/tests/test_bump_find_increment.py +++ b/tests/test_bump_find_increment.py @@ -32,14 +32,14 @@ MAJOR_INCREMENTS_BREAKING_CHANGE_CC = [ "feat(cli): added version", "docs(README): motivation", - "BREAKING CHANGE: `extends` key in config file is now used for extending other config files", # noqa + "BREAKING CHANGE: `extends` key in config file is now used for extending other config files", "fix(setup.py): future is now required for every python version", ] MAJOR_INCREMENTS_BREAKING_CHANGE_ALT_CC = [ "feat(cli): added version", "docs(README): motivation", - "BREAKING-CHANGE: `extends` key in config file is now used for extending other config files", # noqa + "BREAKING-CHANGE: `extends` key in config file is now used for extending other config files", "fix(setup.py): future is now required for every python version", ] diff --git a/tests/test_bump_update_version_in_files.py b/tests/test_bump_update_version_in_files.py index 850b59c166..9d53a3e81d 100644 --- a/tests/test_bump_update_version_in_files.py +++ b/tests/test_bump_update_version_in_files.py @@ -2,6 +2,7 @@ from shutil import copyfile import pytest +from _pytest.fixtures import FixtureRequest from commitizen import bump from commitizen.exceptions import CurrentVersionNotFoundError @@ -21,40 +22,40 @@ def _copy_sample_file_to_tmpdir( @pytest.fixture(scope="function") -def commitizen_config_file(tmpdir): +def commitizen_config_file(tmp_path: Path) -> Path: return _copy_sample_file_to_tmpdir( - tmpdir, "sample_pyproject.toml", "pyproject.toml" + tmp_path, "sample_pyproject.toml", "pyproject.toml" ) @pytest.fixture(scope="function") -def python_version_file(tmpdir, request): - return _copy_sample_file_to_tmpdir(tmpdir, "sample_version.py", "__version__.py") +def python_version_file(tmp_path: Path, request: FixtureRequest) -> Path: + return _copy_sample_file_to_tmpdir(tmp_path, "sample_version.py", "__version__.py") @pytest.fixture(scope="function") -def inconsistent_python_version_file(tmpdir): +def inconsistent_python_version_file(tmp_path: Path) -> Path: return _copy_sample_file_to_tmpdir( - tmpdir, "inconsistent_version.py", "__version__.py" + tmp_path, "inconsistent_version.py", "__version__.py" ) @pytest.fixture(scope="function") -def random_location_version_file(tmpdir): - return _copy_sample_file_to_tmpdir(tmpdir, "sample_cargo.lock", "Cargo.lock") +def random_location_version_file(tmp_path: Path) -> Path: + return _copy_sample_file_to_tmpdir(tmp_path, "sample_cargo.lock", "Cargo.lock") @pytest.fixture(scope="function") -def version_repeated_file(tmpdir): +def version_repeated_file(tmp_path: Path) -> Path: return _copy_sample_file_to_tmpdir( - tmpdir, "repeated_version_number.json", "package.json" + tmp_path, "repeated_version_number.json", "package.json" ) @pytest.fixture(scope="function") -def docker_compose_file(tmpdir): +def docker_compose_file(tmp_path: Path) -> Path: return _copy_sample_file_to_tmpdir( - tmpdir, "sample_docker_compose.yaml", "docker-compose.yaml" + tmp_path, "sample_docker_compose.yaml", "docker-compose.yaml" ) @@ -66,36 +67,38 @@ def docker_compose_file(tmpdir): ), ids=("with_eol", "without_eol"), ) -def multiple_versions_to_update_poetry_lock(tmpdir, request): - return _copy_sample_file_to_tmpdir(tmpdir, request.param, "pyproject.toml") +def multiple_versions_to_update_poetry_lock( + tmp_path: Path, request: FixtureRequest +) -> Path: + return _copy_sample_file_to_tmpdir(tmp_path, request.param, "pyproject.toml") @pytest.fixture(scope="function") -def multiple_versions_increase_string(tmpdir): - tmp_file = tmpdir.join("anyfile") - tmp_file.write(MULTIPLE_VERSIONS_INCREASE_STRING) +def multiple_versions_increase_string(tmp_path: Path) -> str: + tmp_file = tmp_path / "anyfile" + tmp_file.write_text(MULTIPLE_VERSIONS_INCREASE_STRING) return str(tmp_file) @pytest.fixture(scope="function") -def multiple_versions_reduce_string(tmpdir): - tmp_file = tmpdir.join("anyfile") - tmp_file.write(MULTIPLE_VERSIONS_REDUCE_STRING) +def multiple_versions_reduce_string(tmp_path: Path) -> str: + tmp_file = tmp_path / "anyfile" + tmp_file.write_text(MULTIPLE_VERSIONS_REDUCE_STRING) return str(tmp_file) @pytest.fixture(scope="function") def version_files( - commitizen_config_file, - python_version_file, - version_repeated_file, - docker_compose_file, -): + commitizen_config_file: Path, + python_version_file: Path, + version_repeated_file: Path, + docker_compose_file: Path, +) -> tuple[str, ...]: return ( - commitizen_config_file, - python_version_file, - version_repeated_file, - docker_compose_file, + str(commitizen_config_file), + str(python_version_file), + str(version_repeated_file), + str(docker_compose_file), ) @@ -103,7 +106,11 @@ def test_update_version_in_files(version_files, file_regression): old_version = "1.2.3" new_version = "2.0.0" bump.update_version_in_files( - old_version, new_version, version_files, encoding="utf-8" + old_version, + new_version, + version_files, + check_consistency=False, + encoding="utf-8", ) file_contents = "" @@ -119,7 +126,9 @@ def test_partial_update_of_file(version_repeated_file, file_regression): regex = "version" location = f"{version_repeated_file}:{regex}" - bump.update_version_in_files(old_version, new_version, [location], encoding="utf-8") + bump.update_version_in_files( + old_version, new_version, [location], check_consistency=False, encoding="utf-8" + ) with open(version_repeated_file, encoding="utf-8") as f: file_regression.check(f.read(), extension=".json") @@ -129,7 +138,9 @@ def test_random_location(random_location_version_file, file_regression): new_version = "2.0.0" location = f"{random_location_version_file}:version.+Commitizen" - bump.update_version_in_files(old_version, new_version, [location], encoding="utf-8") + bump.update_version_in_files( + old_version, new_version, [location], check_consistency=False, encoding="utf-8" + ) with open(random_location_version_file, encoding="utf-8") as f: file_regression.check(f.read(), extension=".lock") @@ -141,7 +152,9 @@ def test_duplicates_are_change_with_no_regex( new_version = "2.0.0" location = f"{random_location_version_file}:version" - bump.update_version_in_files(old_version, new_version, [location], encoding="utf-8") + bump.update_version_in_files( + old_version, new_version, [location], check_consistency=False, encoding="utf-8" + ) with open(random_location_version_file, encoding="utf-8") as f: file_regression.check(f.read(), extension=".lock") @@ -153,7 +166,9 @@ def test_version_bump_increase_string_length( new_version = "1.2.10" location = f"{multiple_versions_increase_string}:version" - bump.update_version_in_files(old_version, new_version, [location], encoding="utf-8") + bump.update_version_in_files( + old_version, new_version, [location], check_consistency=False, encoding="utf-8" + ) with open(multiple_versions_increase_string, encoding="utf-8") as f: file_regression.check(f.read(), extension=".txt") @@ -165,7 +180,9 @@ def test_version_bump_reduce_string_length( new_version = "2.0.0" location = f"{multiple_versions_reduce_string}:version" - bump.update_version_in_files(old_version, new_version, [location], encoding="utf-8") + bump.update_version_in_files( + old_version, new_version, [location], check_consistency=False, encoding="utf-8" + ) with open(multiple_versions_reduce_string, encoding="utf-8") as f: file_regression.check(f.read(), extension=".txt") @@ -197,14 +214,16 @@ 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" new_version = "1.2.10" location = f"{multiple_versions_to_update_poetry_lock}:version" - bump.update_version_in_files(old_version, new_version, [location], encoding="utf-8") + bump.update_version_in_files( + old_version, new_version, [location], check_consistency=False, encoding="utf-8" + ) with open(multiple_versions_to_update_poetry_lock, encoding="utf-8") as f: file_regression.check(f.read(), extension=".toml") @@ -212,16 +231,165 @@ def test_multiplt_versions_to_bump( def test_update_version_in_globbed_files(commitizen_config_file, file_regression): old_version = "1.2.3" new_version = "2.0.0" - other = commitizen_config_file.dirpath("other.toml") - print(commitizen_config_file, other) + other = commitizen_config_file.parent / "other.toml" + copyfile(commitizen_config_file, other) - # Prepend full ppath as test assume absolute paths or cwd-relative - version_files = [commitizen_config_file.dirpath("*.toml")] + # Prepend full path as test assume absolute paths or cwd-relative + version_files = [ + str(file_path) for file_path in commitizen_config_file.parent.glob("*.toml") + ] bump.update_version_in_files( - old_version, new_version, version_files, encoding="utf-8" + old_version, + new_version, + version_files, + check_consistency=False, + encoding="utf-8", ) for file in commitizen_config_file, other: file_regression.check(file.read_text("utf-8"), extension=".toml") + + +def test_update_version_in_files_with_check_consistency_true( + version_files: tuple[str, ...], +): + """Test update_version_in_files with check_consistency=True (success case).""" + old_version = "1.2.3" + new_version = "2.0.0" + + # This should succeed because all files contain the current version + updated_files: list[str] = bump.update_version_in_files( + old_version, + new_version, + version_files, + check_consistency=True, + encoding="utf-8", + ) + + # Verify that all files were updated + assert set(updated_files) == set(version_files) + + +def test_update_version_in_files_with_check_consistency_true_failure( + commitizen_config_file, inconsistent_python_version_file +): + """Test update_version_in_files with check_consistency=True (failure case).""" + old_version = "1.2.3" + new_version = "2.0.0" + version_files = [commitizen_config_file, inconsistent_python_version_file] + + # This should fail because inconsistent_python_version_file doesn't contain the current version + with pytest.raises(CurrentVersionNotFoundError) as excinfo: + bump.update_version_in_files( + old_version, + new_version, + version_files, + check_consistency=True, + encoding="utf-8", + ) + + expected_msg = ( + f"Current version {old_version} is not found in {inconsistent_python_version_file}.\n" + "The version defined in commitizen configuration and the ones in " + "version_files are possibly inconsistent." + ) + assert expected_msg in str(excinfo.value) + + +@pytest.mark.parametrize( + "encoding,filename", + [ + ("latin-1", "test_latin1.txt"), + ("utf-16", "test_utf16.txt"), + ], + ids=["latin-1", "utf-16"], +) +def test_update_version_in_files_with_different_encodings(tmp_path, encoding, filename): + """Test update_version_in_files with different encodings.""" + # Create a test file with the specified encoding + test_file = tmp_path / filename + content = f'version = "1.2.3"\n# This is a test file with {encoding} encoding\n' + test_file.write_text(content, encoding=encoding) + + old_version = "1.2.3" + new_version = "2.0.0" + + updated_files = bump.update_version_in_files( + old_version, + new_version, + [str(test_file)], + check_consistency=True, + encoding=encoding, + ) + + # Verify the file was updated + assert len(updated_files) == 1 + assert str(test_file) in updated_files + + # Verify the content was updated correctly + updated_content = test_file.read_text(encoding=encoding) + assert f'version = "{new_version}"' in updated_content + assert f'version = "{old_version}"' not in updated_content + + +def test_update_version_in_files_return_value(version_files): + """Test that update_version_in_files returns the correct list of updated files.""" + old_version = "1.2.3" + new_version = "2.0.0" + + updated_files = bump.update_version_in_files( + old_version, + new_version, + version_files, + check_consistency=False, + encoding="utf-8", + ) + + # Verify return value is a list + assert isinstance(updated_files, list) + + # Verify all files in the input are in the returned list + assert set(version_files) == set(updated_files) + + # Verify the returned paths are strings + assert all(isinstance(file_path, str) for file_path in updated_files) + + +def test_update_version_in_files_return_value_partial_update(tmp_path): + """Test return value when only some files are updated.""" + # Create two test files + file1 = tmp_path / "file1.txt" + file2 = tmp_path / "file2.txt" + + # File1 contains the version to update + file1.write_text('version = "1.2.3"\n') + + # File2 doesn't contain the version + file2.write_text("some other content\n") + + old_version = "1.2.3" + new_version = "2.0.0" + + updated_files = bump.update_version_in_files( + old_version, + new_version, + [str(file1), str(file2)], + check_consistency=False, + encoding="utf-8", + ) + + # Verify return value + assert isinstance(updated_files, list) + assert len(updated_files) == 2 # Both files should be in the list + assert str(file1) in updated_files + assert str(file2) in updated_files + + # Verify file1 was actually updated + content1 = file1.read_text(encoding="utf-8") + assert f'version = "{new_version}"' in content1 + + # Verify file2 was not changed + content2 = file2.read_text(encoding="utf-8") + assert content2 == "some other content\n" diff --git a/tests/test_changelog.py b/tests/test_changelog.py index 67ba273b5c..e32a9bcf9e 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -4,14 +4,13 @@ import re from dataclasses import dataclass from pathlib import Path -from typing import Any +from typing import TYPE_CHECKING, Any from unittest.mock import Mock import pytest from jinja2 import FileSystemLoader from commitizen import changelog, git -from commitizen.changelog_formats import ChangelogFormat from commitizen.commands.changelog import Changelog from commitizen.config import BaseConfig from commitizen.cz.conventional_commits.conventional_commits import ( @@ -20,6 +19,9 @@ from commitizen.exceptions import InvalidConfigurationError from commitizen.version_schemes import Pep440 +if TYPE_CHECKING: + from commitizen.changelog_formats import ChangelogFormat + COMMITS_DATA: list[dict[str, Any]] = [ { "rev": "141ee441c9c9da0809c554103a558eb17c30ed17", @@ -556,8 +558,8 @@ @pytest.fixture -def gitcommits() -> list: - commits = [ +def gitcommits() -> list[git.GitCommit]: + return [ git.GitCommit( commit["rev"], commit["title"], @@ -568,13 +570,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 @@ -1217,28 +1217,28 @@ def test_generate_tree_from_commits_with_no_commits(tags): ), ), ) -def test_order_changelog_tree(change_type_order, expected_reordering): - tree = changelog.order_changelog_tree(COMMITS_TREE, change_type_order) +def test_generate_ordered_changelog_tree(change_type_order, expected_reordering): + tree = changelog.generate_ordered_changelog_tree(COMMITS_TREE, change_type_order) for index, entry in enumerate(tuple(tree)): - version = tree[index]["version"] + version = entry["version"] if version in expected_reordering: # Verify that all keys are present - assert [*tree[index].keys()] == [*COMMITS_TREE[index].keys()] + assert [*entry.keys()] == [*COMMITS_TREE[index].keys()] # Verify that the reorder only impacted the returned dict and not the original expected = expected_reordering[version] - assert [*tree[index]["changes"].keys()] == expected["sorted"] + assert [*entry["changes"].keys()] == expected["sorted"] assert [*COMMITS_TREE[index]["changes"].keys()] == expected["original"] else: - assert [*entry["changes"].keys()] == [*tree[index]["changes"].keys()] + assert [*entry["changes"].keys()] == [*entry["changes"].keys()] -def test_order_changelog_tree_raises(): +def test_generate_ordered_changelog_tree_raises(): change_type_order = ["BREAKING CHANGE", "feat", "refactor", "feat"] with pytest.raises(InvalidConfigurationError) as excinfo: - changelog.order_changelog_tree(COMMITS_TREE, change_type_order) + list(changelog.generate_ordered_changelog_tree(COMMITS_TREE, change_type_order)) - assert "Change types contain duplicates types" in str(excinfo) + assert "Change types contain duplicated types" in str(excinfo) def test_render_changelog( @@ -1537,6 +1537,24 @@ def test_get_smart_tag_range_returns_an_extra_for_a_single_tag(tags): assert 2 == len(res) +def test_get_next_tag_name_after_version(tags): + # Test finding next tag after a version + next_tag_name = changelog.get_next_tag_name_after_version(tags, "v1.2.0") + assert next_tag_name == "v1.1.1" + + next_tag_name = changelog.get_next_tag_name_after_version(tags, "v1.1.0") + assert next_tag_name == "v1.0.0" + + # Test finding last tag when given version is last + last_tag_name = changelog.get_next_tag_name_after_version(tags, "v0.9.1") + assert last_tag_name is None + + # Test error when version not found + with pytest.raises(changelog.NoCommitsFoundError) as exc_info: + changelog.get_next_tag_name_after_version(tags, "nonexistent") + assert "Could not find a valid revision range" in str(exc_info.value) + + @dataclass class TagDef: name: str @@ -1635,13 +1653,15 @@ 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 def test_changelog_file_name_from_args_and_config(): mock_config = Mock(spec=BaseConfig) - mock_config.path.parent = "/my/project" + mock_path = Mock(spec=Path) + mock_path.parent = Path("/my/project") + mock_config.path = mock_path mock_config.settings = { "name": "cz_conventional_commits", "changelog_file": "CHANGELOG.md", diff --git a/tests/test_changelog_format_asciidoc.py b/tests/test_changelog_format_asciidoc.py index 59ca56191e..199c4b9729 100644 --- a/tests/test_changelog_format_asciidoc.py +++ b/tests/test_changelog_format_asciidoc.py @@ -1,12 +1,16 @@ from __future__ import annotations -from pathlib import Path +from typing import TYPE_CHECKING import pytest from commitizen.changelog import Metadata from commitizen.changelog_formats.asciidoc import AsciiDoc -from commitizen.config.base_config import BaseConfig + +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig CHANGELOG_A = """ = Changelog @@ -160,7 +164,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..e09d68cfed 100644 --- a/tests/test_changelog_format_markdown.py +++ b/tests/test_changelog_format_markdown.py @@ -1,12 +1,16 @@ from __future__ import annotations -from pathlib import Path +from typing import TYPE_CHECKING import pytest from commitizen.changelog import Metadata from commitizen.changelog_formats.markdown import Markdown -from commitizen.config.base_config import BaseConfig + +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig CHANGELOG_A = """ # Changelog @@ -160,7 +164,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..e6eceff4fa 100644 --- a/tests/test_changelog_format_restructuredtext.py +++ b/tests/test_changelog_format_restructuredtext.py @@ -1,18 +1,24 @@ from __future__ import annotations -from pathlib import Path from textwrap import dedent from typing import TYPE_CHECKING import pytest from commitizen.changelog import Metadata -from commitizen.changelog_formats.restructuredtext import RestructuredText -from commitizen.config.base_config import BaseConfig +from commitizen.changelog_formats.restructuredtext import ( + RestructuredText, + _is_overlined_title, + _is_underlined_title, +) if TYPE_CHECKING: + from pathlib import Path + from _pytest.mark.structures import ParameterSet + from commitizen.config.base_config import BaseConfig + CASES: list[ParameterSet] = [] @@ -311,7 +317,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 @@ -325,9 +331,9 @@ def test_get_matadata( [(text, True) for text in UNDERLINED_TITLES] + [(text, False) for text in NOT_UNDERLINED_TITLES], ) -def test_is_underlined_title(format: RestructuredText, text: str, expected: bool): +def test_is_underlined_title(text: str, expected: bool): _, first, second = dedent(text).splitlines() - assert format.is_underlined_title(first, second) is expected + assert _is_underlined_title(first, second) is expected @pytest.mark.parametrize( @@ -335,10 +341,10 @@ def test_is_underlined_title(format: RestructuredText, text: str, expected: bool [(text, True) for text in OVERLINED_TITLES] + [(text, False) for text in NOT_OVERLINED_TITLES], ) -def test_is_overlined_title(format: RestructuredText, text: str, expected: bool): +def test_is_overlined_title(text: str, expected: bool): _, first, second, third = dedent(text).splitlines() - assert format.is_overlined_title(first, second, third) is expected + assert _is_overlined_title(first, second, third) is expected @pytest.mark.parametrize( diff --git a/tests/test_changelog_format_textile.py b/tests/test_changelog_format_textile.py index eb03484ad5..752cf229e5 100644 --- a/tests/test_changelog_format_textile.py +++ b/tests/test_changelog_format_textile.py @@ -1,12 +1,16 @@ from __future__ import annotations -from pathlib import Path +from typing import TYPE_CHECKING import pytest from commitizen.changelog import Metadata from commitizen.changelog_formats.textile import Textile -from commitizen.config.base_config import BaseConfig + +if TYPE_CHECKING: + from pathlib import Path + + from commitizen.config.base_config import BaseConfig CHANGELOG_A = """ h1. Changelog @@ -153,7 +157,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_changelog_formats.py b/tests/test_changelog_formats.py index dec23720dc..ff7126d345 100644 --- a/tests/test_changelog_formats.py +++ b/tests/test_changelog_formats.py @@ -1,28 +1,32 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import pytest from commitizen import defaults from commitizen.changelog_formats import ( KNOWN_CHANGELOG_FORMATS, ChangelogFormat, + _guess_changelog_format, get_changelog_format, - guess_changelog_format, ) -from commitizen.config.base_config import BaseConfig from commitizen.exceptions import ChangelogFormatUnknown +if TYPE_CHECKING: + from commitizen.config.base_config import BaseConfig + @pytest.mark.parametrize("format", KNOWN_CHANGELOG_FORMATS.values()) def test_guess_format(format: type[ChangelogFormat]): - assert guess_changelog_format(f"CHANGELOG.{format.extension}") is format + assert _guess_changelog_format(f"CHANGELOG.{format.extension}") is format for ext in format.alternative_extensions: - assert guess_changelog_format(f"CHANGELOG.{ext}") is format + assert _guess_changelog_format(f"CHANGELOG.{ext}") is format @pytest.mark.parametrize("filename", ("CHANGELOG", "NEWS", "file.unknown", None)) def test_guess_format_unknown(filename: str): - assert guess_changelog_format(filename) is None + assert _guess_changelog_format(filename) is None @pytest.mark.parametrize( diff --git a/tests/test_cli.py b/tests/test_cli.py index a91e633128..f91a27373a 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,6 +1,7 @@ import os import subprocess import sys +import types from functools import partial import pytest @@ -182,3 +183,59 @@ def test_unknown_args_before_double_dash_raises(mocker: MockFixture): assert "Invalid commitizen arguments were found before -- separator" in str( excinfo.value ) + + +def test_commitizen_excepthook_non_commitizen_exception(mocker: MockFixture): + """Test that commitizen_excepthook delegates to sys.__excepthook__ for non-CommitizenException.""" + # Mock the original excepthook + mock_original_excepthook = mocker.Mock() + mocker.patch("commitizen.cli.sys.__excepthook__", mock_original_excepthook) + + # Create a regular exception + test_exception = ValueError("test error") + + # Call commitizen_excepthook with the regular exception + cli.commitizen_excepthook(ValueError, test_exception, None) + + # Verify sys.__excepthook__ was called with correct arguments + mock_original_excepthook.assert_called_once_with(ValueError, test_exception, None) + + +def test_commitizen_excepthook_non_commitizen_exception_with_traceback( + mocker: MockFixture, +): + """Test that commitizen_excepthook handles traceback correctly for non-CommitizenException.""" + # Mock the original excepthook + mock_original_excepthook = mocker.Mock() + mocker.patch("commitizen.cli.sys.__excepthook__", mock_original_excepthook) + + # Create a regular exception with a traceback + test_exception = ValueError("test error") + test_traceback = mocker.Mock(spec=types.TracebackType) + + # Call commitizen_excepthook with the regular exception and traceback + cli.commitizen_excepthook(ValueError, test_exception, test_traceback) + + # Verify sys.__excepthook__ was called with correct arguments including traceback + mock_original_excepthook.assert_called_once_with( + ValueError, test_exception, test_traceback + ) + + +def test_commitizen_excepthook_non_commitizen_exception_with_invalid_traceback( + mocker: MockFixture, +): + """Test that commitizen_excepthook handles invalid traceback correctly for non-CommitizenException.""" + # Mock the original excepthook + mock_original_excepthook = mocker.Mock() + mocker.patch("commitizen.cli.sys.__excepthook__", mock_original_excepthook) + + # Create a regular exception with an invalid traceback + test_exception = ValueError("test error") + test_traceback = mocker.Mock() # Not a TracebackType + + # Call commitizen_excepthook with the regular exception and invalid traceback + cli.commitizen_excepthook(ValueError, test_exception, test_traceback) + + # Verify sys.__excepthook__ was called with None as traceback + mock_original_excepthook.assert_called_once_with(ValueError, test_exception, None) diff --git a/tests/test_conf.py b/tests/test_conf.py index 80d58983e7..bbbed41e08 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -9,6 +9,9 @@ import yaml from commitizen import config, defaults, git +from commitizen.config.json_config import JsonConfig +from commitizen.config.toml_config import TomlConfig +from commitizen.config.yaml_config import YAMLConfig from commitizen.exceptions import ConfigFileIsEmpty, InvalidConfigurationError PYPROJECT = """ @@ -77,7 +80,14 @@ "bump_message": None, "retry_after_failure": False, "allow_abort": False, - "allowed_prefixes": ["Merge", "Revert", "Pull request", "fixup!", "squash!"], + "allowed_prefixes": [ + "Merge", + "Revert", + "Pull request", + "fixup!", + "squash!", + "amend!", + ], "version_files": ["commitizen/__version__.py", "pyproject.toml"], "style": [["pointer", "reverse"], ["question", "underline"]], "changelog_file": "CHANGELOG.md", @@ -95,6 +105,8 @@ "always_signoff": False, "template": None, "extras": {}, + "breaking_change_exclamation_in_title": False, + "message_length_limit": None, } _new_settings: dict[str, Any] = { @@ -108,7 +120,14 @@ "bump_message": None, "retry_after_failure": False, "allow_abort": False, - "allowed_prefixes": ["Merge", "Revert", "Pull request", "fixup!", "squash!"], + "allowed_prefixes": [ + "Merge", + "Revert", + "Pull request", + "fixup!", + "squash!", + "amend!", + ], "version_files": ["commitizen/__version__.py", "pyproject.toml"], "style": [["pointer", "reverse"], ["question", "underline"]], "changelog_file": "CHANGELOG.md", @@ -126,6 +145,8 @@ "always_signoff": False, "template": None, "extras": {}, + "breaking_change_exclamation_in_title": False, + "message_length_limit": None, } @@ -151,7 +172,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 +183,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() @@ -197,7 +218,7 @@ def test_load_cz_json_not_from_config_argument(_, tmpdir): _not_root_path.write(JSON_STR) cfg = config.read_cfg(filepath="./not_in_root/.cz.json") - json_cfg_by_class = config.JsonConfig(data=JSON_STR, path=_not_root_path) + json_cfg_by_class = JsonConfig(data=JSON_STR, path=_not_root_path) assert cfg.settings == json_cfg_by_class.settings def test_load_cz_yaml_not_from_config_argument(_, tmpdir): @@ -206,7 +227,7 @@ def test_load_cz_yaml_not_from_config_argument(_, tmpdir): _not_root_path.write(YAML_STR) cfg = config.read_cfg(filepath="./not_in_root/.cz.yaml") - yaml_cfg_by_class = config.YAMLConfig(data=YAML_STR, path=_not_root_path) + yaml_cfg_by_class = YAMLConfig(data=YAML_STR, path=_not_root_path) assert cfg.settings == yaml_cfg_by_class._settings def test_load_empty_pyproject_toml_from_config_argument(_, tmpdir): @@ -230,7 +251,7 @@ def test_load_empty_pyproject_toml_from_config_argument(_, tmpdir): class TestTomlConfig: def test_init_empty_config_content(self, tmpdir, config_file, exception_string): path = tmpdir.mkdir("commitizen").join(config_file) - toml_config = config.TomlConfig(data="", path=path) + toml_config = TomlConfig(data="", path=path) toml_config.init_empty_config_content() with open(path, encoding="utf-8") as toml_file: @@ -243,7 +264,7 @@ def test_init_empty_config_content_with_existing_content( path = tmpdir.mkdir("commitizen").join(config_file) path.write(existing_content) - toml_config = config.TomlConfig(data="", path=path) + toml_config = TomlConfig(data="", path=path) toml_config.init_empty_config_content() with open(path, encoding="utf-8") as toml_file: @@ -256,7 +277,7 @@ def test_init_with_invalid_config_content( path = tmpdir.mkdir("commitizen").join(config_file) with pytest.raises(InvalidConfigurationError, match=exception_string): - config.TomlConfig(data=existing_content, path=path) + TomlConfig(data=existing_content, path=path) @pytest.mark.parametrize( @@ -270,7 +291,7 @@ def test_init_with_invalid_config_content( class TestJsonConfig: def test_init_empty_config_content(self, tmpdir, config_file, exception_string): path = tmpdir.mkdir("commitizen").join(config_file) - json_config = config.JsonConfig(data="{}", path=path) + json_config = JsonConfig(data="{}", path=path) json_config.init_empty_config_content() with open(path, encoding="utf-8") as json_file: @@ -283,7 +304,7 @@ def test_init_with_invalid_config_content( path = tmpdir.mkdir("commitizen").join(config_file) with pytest.raises(InvalidConfigurationError, match=exception_string): - config.JsonConfig(data=existing_content, path=path) + JsonConfig(data=existing_content, path=path) @pytest.mark.parametrize( @@ -297,7 +318,7 @@ def test_init_with_invalid_config_content( class TestYamlConfig: def test_init_empty_config_content(self, tmpdir, config_file, exception_string): path = tmpdir.mkdir("commitizen").join(config_file) - yaml_config = config.YAMLConfig(data="{}", path=path) + yaml_config = YAMLConfig(data="{}", path=path) yaml_config.init_empty_config_content() with open(path) as yaml_file: @@ -308,4 +329,4 @@ def test_init_with_invalid_content(self, tmpdir, config_file, exception_string): path = tmpdir.mkdir("commitizen").join(config_file) with pytest.raises(InvalidConfigurationError, match=exception_string): - config.YAMLConfig(data=existing_content, path=path) + YAMLConfig(data=existing_content, path=path) diff --git a/tests/test_cz_base.py b/tests/test_cz_base.py deleted file mode 100644 index 4ee1cc6eda..0000000000 --- a/tests/test_cz_base.py +++ /dev/null @@ -1,50 +0,0 @@ -import pytest - -from commitizen.cz.base import BaseCommitizen - - -class DummyCz(BaseCommitizen): - def questions(self): - return [{"type": "input", "name": "commit", "message": "Initial commit:\n"}] - - def message(self, answers: dict): - return answers["commit"] - - -def test_base_raises_error(config): - with pytest.raises(TypeError): - BaseCommitizen(config) - - -def test_questions(config): - cz = DummyCz(config) - assert isinstance(cz.questions(), list) - - -def test_message(config): - cz = DummyCz(config) - assert cz.message({"commit": "holis"}) == "holis" - - -def test_example(config): - cz = DummyCz(config) - with pytest.raises(NotImplementedError): - cz.example() - - -def test_schema(config): - cz = DummyCz(config) - with pytest.raises(NotImplementedError): - cz.schema() - - -def test_info(config): - cz = DummyCz(config) - with pytest.raises(NotImplementedError): - cz.info() - - -def test_process_commit(config): - cz = DummyCz(config) - message = cz.process_commit("test(test_scope): this is test msg") - assert message == "test(test_scope): this is test msg" diff --git a/tests/test_cz_conventional_commits.py b/tests/test_cz_conventional_commits.py index 6d4e0f7435..1742b0f3b7 100644 --- a/tests/test_cz_conventional_commits.py +++ b/tests/test_cz_conventional_commits.py @@ -2,48 +2,42 @@ from commitizen.cz.conventional_commits.conventional_commits import ( ConventionalCommitsCz, - parse_scope, - parse_subject, + _parse_scope, + _parse_subject, ) from commitizen.cz.exceptions import AnswerRequiredError -valid_scopes = ["", "simple", "dash-separated", "camelCaseUPPERCASE"] -scopes_transformations = [["with spaces", "with-spaces"], [None, ""]] - -valid_subjects = ["this is a normal text", "aword"] - -subjects_transformations = [["with dot.", "with dot"]] - -invalid_subjects = ["", " ", ".", " .", "", None] - - -def test_parse_scope_valid_values(): - for valid_scope in valid_scopes: - assert valid_scope == parse_scope(valid_scope) +@pytest.mark.parametrize( + "valid_scope", ["", "simple", "dash-separated", "camelCaseUPPERCASE"] +) +def test_parse_scope_valid_values(valid_scope): + assert valid_scope == _parse_scope(valid_scope) -def test_scopes_transformations(): - for scopes_transformation in scopes_transformations: - invalid_scope, transformed_scope = scopes_transformation - assert transformed_scope == parse_scope(invalid_scope) +@pytest.mark.parametrize( + "scopes_transformation", [["with spaces", "with-spaces"], ["", ""]] +) +def test_scopes_transformations(scopes_transformation): + invalid_scope, transformed_scope = scopes_transformation + assert transformed_scope == _parse_scope(invalid_scope) -def test_parse_subject_valid_values(): - for valid_subject in valid_subjects: - assert valid_subject == parse_subject(valid_subject) +@pytest.mark.parametrize("valid_subject", ["this is a normal text", "aword"]) +def test_parse_subject_valid_values(valid_subject): + assert valid_subject == _parse_subject(valid_subject) -def test_parse_subject_invalid_values(): - for valid_subject in invalid_subjects: - with pytest.raises(AnswerRequiredError): - parse_subject(valid_subject) +@pytest.mark.parametrize("invalid_subject", ["", " ", ".", " .", "\t\t."]) +def test_parse_subject_invalid_values(invalid_subject): + with pytest.raises(AnswerRequiredError): + _parse_subject(invalid_subject) -def test_subject_transformations(): - for subject_transformation in subjects_transformations: - invalid_subject, transformed_subject = subject_transformation - assert transformed_subject == parse_subject(invalid_subject) +@pytest.mark.parametrize("subject_transformation", [["with dot.", "with dot"]]) +def test_subject_transformations(subject_transformation): + invalid_subject, transformed_subject = subject_transformation + assert transformed_subject == _parse_subject(invalid_subject) def test_questions(config): @@ -89,7 +83,7 @@ def test_long_answer(config): message = conventional_commits.message(answers) assert ( message - == "fix(users): email pattern corrected\n\ncomplete content\n\ncloses #24" # noqa + == "fix(users): email pattern corrected\n\ncomplete content\n\ncloses #24" ) @@ -107,8 +101,57 @@ def test_breaking_change_in_footer(config): print(message) assert ( message - == "fix(users): email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users" # noqa + == "fix(users): email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users" + ) + + +@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): @@ -130,26 +173,3 @@ def test_info(config): conventional_commits = ConventionalCommitsCz(config) info = conventional_commits.info() assert isinstance(info, str) - - -@pytest.mark.parametrize( - ("commit_message", "expected_message"), - [ - ( - "test(test_scope): this is test msg", - "this is test msg", - ), - ( - "test(test_scope)!: this is test msg", - "this is test msg", - ), - ( - "test!(test_scope): this is test msg", - "", - ), - ], -) -def test_process_commit(commit_message, expected_message, config): - conventional_commits = ConventionalCommitsCz(config) - message = conventional_commits.process_commit(commit_message) - assert message == expected_message diff --git a/tests/test_cz_customize.py b/tests/test_cz_customize.py index 210c8b6774..dd354d65ea 100644 --- a/tests/test_cz_customize.py +++ b/tests/test_cz_customize.py @@ -1,6 +1,11 @@ +from pathlib import Path + import pytest -from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig +from commitizen.config import BaseConfig +from commitizen.config.json_config import JsonConfig +from commitizen.config.toml_config import TomlConfig +from commitizen.config.yaml_config import YAMLConfig from commitizen.cz.customize import CustomizeCommitsCz from commitizen.exceptions import MissingCzCustomizeConfigError @@ -105,17 +110,22 @@ - commitizen/__version__.py - pyproject.toml customize: - message_template: "{{change_type}}:{% if show_message %} {{message}}{% endif %}" + message_template: '{{change_type}}:{% if show_message %} {{message}}{% endif %}' example: 'feature: this feature enables customization through a config file' - schema: "<type>: <body>" - schema_pattern: "(feature|bug fix):(\\s.*)" - bump_pattern: "^(break|new|fix|hotfix)" + schema: '<type>: <body>' + schema_pattern: '(feature|bug fix):(\\s.*)' + bump_pattern: '^(break|new|fix|hotfix)' + commit_parser: '^(?P<change_type>feature|bug fix):\\s(?P<message>.*)?' + changelog_pattern: '^(feature|bug fix)?(!)?' + change_type_map: + feature: Feat + bug fix: Fix bump_map: break: MAJOR new: MINOR fix: PATCH hotfix: PATCH - change_type_order: ["perf", "BREAKING CHANGE", "feat", "fix", "refactor"] + change_type_order: ['perf', 'BREAKING CHANGE', 'feat', 'fix', 'refactor'] info: This is a customized cz. questions: - type: list @@ -319,8 +329,9 @@ @pytest.fixture( params=[ - TomlConfig(data=TOML_STR, path="not_exist.toml"), - JsonConfig(data=JSON_STR, path="not_exist.json"), + TomlConfig(data=TOML_STR, path=Path("not_exist.toml")), + JsonConfig(data=JSON_STR, path=Path("not_exist.json")), + YAMLConfig(data=YAML_STR, path=Path("not_exist.yaml")), ] ) def config(request): @@ -334,9 +345,9 @@ def config(request): @pytest.fixture( params=[ - TomlConfig(data=TOML_STR_INFO_PATH, path="not_exist.toml"), - JsonConfig(data=JSON_STR_INFO_PATH, path="not_exist.json"), - YAMLConfig(data=YAML_STR_INFO_PATH, path="not_exist.yaml"), + TomlConfig(data=TOML_STR_INFO_PATH, path=Path("not_exist.toml")), + JsonConfig(data=JSON_STR_INFO_PATH, path=Path("not_exist.json")), + YAMLConfig(data=YAML_STR_INFO_PATH, path=Path("not_exist.yaml")), ] ) def config_info(request): @@ -345,9 +356,9 @@ def config_info(request): @pytest.fixture( params=[ - TomlConfig(data=TOML_STR_WITHOUT_INFO, path="not_exist.toml"), - JsonConfig(data=JSON_STR_WITHOUT_PATH, path="not_exist.json"), - YAMLConfig(data=YAML_STR_WITHOUT_PATH, path="not_exist.yaml"), + TomlConfig(data=TOML_STR_WITHOUT_INFO, path=Path("not_exist.toml")), + JsonConfig(data=JSON_STR_WITHOUT_PATH, path=Path("not_exist.json")), + YAMLConfig(data=YAML_STR_WITHOUT_PATH, path=Path("not_exist.yaml")), ] ) def config_without_info(request): @@ -356,8 +367,8 @@ def config_without_info(request): @pytest.fixture( params=[ - TomlConfig(data=TOML_WITH_UNICODE, path="not_exist.toml"), - JsonConfig(data=JSON_WITH_UNICODE, path="not_exist.json"), + TomlConfig(data=TOML_WITH_UNICODE, path=Path("not_exist.toml")), + JsonConfig(data=JSON_WITH_UNICODE, path=Path("not_exist.json")), ] ) def config_with_unicode(request): @@ -473,16 +484,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_cz_search_filter.py b/tests/test_cz_search_filter.py index 0e70e3104a..cbceb8b887 100644 --- a/tests/test_cz_search_filter.py +++ b/tests/test_cz_search_filter.py @@ -1,6 +1,6 @@ import pytest -from commitizen.config import TomlConfig +from commitizen.config.toml_config import TomlConfig from commitizen.cz.customize import CustomizeCommitsCz TOML_WITH_SEARCH_FILTER = r""" diff --git a/tests/test_deprecated.py b/tests/test_deprecated.py new file mode 100644 index 0000000000..41bea81a73 --- /dev/null +++ b/tests/test_deprecated.py @@ -0,0 +1,33 @@ +import pytest + +from commitizen import changelog_formats, defaults + + +def test_getattr_deprecated_vars(): + # Test each deprecated variable + with pytest.warns(DeprecationWarning) as record: + assert defaults.bump_pattern == defaults.BUMP_PATTERN + assert defaults.bump_map == defaults.BUMP_MAP + assert ( + defaults.bump_map_major_version_zero == defaults.BUMP_MAP_MAJOR_VERSION_ZERO + ) + assert defaults.bump_message == defaults.BUMP_MESSAGE + assert defaults.change_type_order == defaults.CHANGE_TYPE_ORDER + assert defaults.encoding == defaults.ENCODING + assert defaults.name == defaults.DEFAULT_SETTINGS["name"] + assert ( + changelog_formats._guess_changelog_format + == changelog_formats.guess_changelog_format + ) + + # Verify warning messages + assert len(record) == 7 + for warning in record: + assert "is deprecated and will be removed" in str(warning.message) + + +def test_getattr_non_existent(): + # Test non-existent attribute + with pytest.raises(AttributeError) as exc_info: + _ = defaults.non_existent_attribute + assert "is not an attribute of" in str(exc_info.value) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py new file mode 100644 index 0000000000..1a66c79d02 --- /dev/null +++ b/tests/test_exceptions.py @@ -0,0 +1,38 @@ +import pytest + +from commitizen.exceptions import ExitCode + + +def test_from_str_with_decimal(): + """Test from_str with decimal values.""" + assert ExitCode.from_str("0") == ExitCode.EXPECTED_EXIT + assert ExitCode.from_str("1") == ExitCode.NO_COMMITIZEN_FOUND + assert ExitCode.from_str("32") == ExitCode.COMMIT_MESSAGE_LENGTH_LIMIT_EXCEEDED + + +def test_from_str_with_enum_name(): + """Test from_str with enum names.""" + assert ExitCode.from_str("EXPECTED_EXIT") == ExitCode.EXPECTED_EXIT + assert ExitCode.from_str("NO_COMMITIZEN_FOUND") == ExitCode.NO_COMMITIZEN_FOUND + assert ( + ExitCode.from_str("COMMIT_MESSAGE_LENGTH_LIMIT_EXCEEDED") + == ExitCode.COMMIT_MESSAGE_LENGTH_LIMIT_EXCEEDED + ) + + +def test_from_str_with_whitespace(): + """Test from_str with whitespace in enum names.""" + assert ExitCode.from_str(" EXPECTED_EXIT ") == ExitCode.EXPECTED_EXIT + assert ExitCode.from_str("\tNO_COMMITIZEN_FOUND\t") == ExitCode.NO_COMMITIZEN_FOUND + + +def test_from_str_with_invalid_values(): + """Test from_str with invalid values.""" + with pytest.raises(KeyError): + ExitCode.from_str("invalid_name") + with pytest.raises(ValueError): + ExitCode.from_str("999") # Out of range decimal + with pytest.raises(KeyError): + ExitCode.from_str("") + with pytest.raises(KeyError): + ExitCode.from_str(" ") 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_git.py b/tests/test_git.py index 8b2fc2b86e..45e6028c9e 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -4,11 +4,12 @@ import os import platform import shutil +from typing import TYPE_CHECKING import pytest -from pytest_mock import MockFixture -from commitizen import cmd, exceptions, git +from commitizen import cmd, git +from commitizen.exceptions import GitCommandError from tests.utils import ( FakeCommand, create_branch, @@ -17,6 +18,16 @@ switch_branch, ) +if TYPE_CHECKING: + from pytest_mock import MockFixture + + +@pytest.mark.parametrize("date", ["2020-01-21", "1970-01-01"]) +def test_git_tag_date(date: str): + git_tag = git.GitTag(rev="sha1-code", name="0.0.1", date="2025-05-30") + git_tag.date = date + assert git_tag.date == date + def test_git_object_eq(): git_commit = git.GitCommit( @@ -79,8 +90,7 @@ def test_get_reachable_tags_with_commits( monkeypatch.setenv("LANGUAGE", f"{locale}.UTF-8") monkeypatch.setenv("LC_ALL", f"{locale}.UTF-8") with tmp_commitizen_project.as_cwd(): - tags = git.get_tags(reachable_only=True) - assert tags == [] + assert git.get_tags(reachable_only=True) == [] def test_get_tag_names(mocker: MockFixture): @@ -104,10 +114,13 @@ def test_git_message_with_empty_body(): @pytest.mark.usefixtures("tmp_commitizen_project") def test_get_log_as_str_list_empty(): - """ensure an exception or empty list in an empty project""" + """ + Ensure an exception is raised or empty list in an empty project. + The behavior is different depending on the version of git. + """ try: gitlog = git._get_log_as_str_list(start=None, end="HEAD", args="") - except exceptions.GitCommandError: + except GitCommandError: return assert len(gitlog) == 0, "list should be empty if no assert" @@ -271,7 +284,7 @@ def test_get_commits_with_signature(): def test_get_tag_names_has_correct_arrow_annotation(): arrow_annotation = inspect.getfullargspec(git.get_tag_names).annotations["return"] - assert arrow_annotation == "list[str | None]" + assert arrow_annotation == "list[str]" def test_get_latest_tag_name(tmp_commitizen_project): @@ -317,24 +330,18 @@ def test_is_staging_clean_when_updating_file(tmp_commitizen_project): assert git.is_staging_clean() is False -def test_git_eol_style(tmp_commitizen_project): +def test_get_eol_for_open(tmp_commitizen_project): with tmp_commitizen_project.as_cwd(): - assert git.get_eol_style() == git.EOLTypes.NATIVE + assert git.EOLType.for_open() == os.linesep cmd.run("git config core.eol lf") - assert git.get_eol_style() == git.EOLTypes.LF + assert git.EOLType.for_open() == "\n" cmd.run("git config core.eol crlf") - assert git.get_eol_style() == git.EOLTypes.CRLF + assert git.EOLType.for_open() == "\r\n" cmd.run("git config core.eol native") - assert git.get_eol_style() == git.EOLTypes.NATIVE - - -def test_eoltypes_get_eol_for_open(): - assert git.EOLTypes.get_eol_for_open(git.EOLTypes.NATIVE) == os.linesep - assert git.EOLTypes.get_eol_for_open(git.EOLTypes.LF) == "\n" - assert git.EOLTypes.get_eol_for_open(git.EOLTypes.CRLF) == "\r\n" + assert git.EOLType.for_open() == os.linesep def test_get_core_editor(mocker): @@ -401,3 +408,101 @@ def test_commit_with_spaces_in_path(mocker, file_path, expected_cmd): mock_run.assert_called_once_with(expected_cmd) mock_unlink.assert_called_once_with(file_path) + + +def test_get_filenames_in_commit_error(mocker: MockFixture): + """Test that GitCommandError is raised when git command fails.""" + mocker.patch( + "commitizen.cmd.run", + return_value=FakeCommand(out="", err="fatal: bad object HEAD", return_code=1), + ) + with pytest.raises(GitCommandError) as excinfo: + git.get_filenames_in_commit() + assert str(excinfo.value) == "fatal: bad object HEAD" + + +def test_git_commit_from_rev_and_commit(): + # Test data with all fields populated + rev_and_commit = ( + "abc123\n" # rev + "def456 ghi789\n" # parents + "feat: add new feature\n" # title + "John Doe\n" # author + "john@example.com\n" # author_email + "This is a detailed description\n" # body + "of the new feature\n" + "with multiple lines" + ) + + commit = git.GitCommit.from_rev_and_commit(rev_and_commit) + + assert commit.rev == "abc123" + assert commit.title == "feat: add new feature" + assert ( + commit.body + == "This is a detailed description\nof the new feature\nwith multiple lines" + ) + assert commit.author == "John Doe" + assert commit.author_email == "john@example.com" + assert commit.parents == ["def456", "ghi789"] + + # Test with minimal data + minimal_commit = ( + "abc123\n" # rev + "\n" # no parents + "feat: minimal commit\n" # title + "John Doe\n" # author + "john@example.com\n" # author_email + ) + + commit = git.GitCommit.from_rev_and_commit(minimal_commit) + + assert commit.rev == "abc123" + assert commit.title == "feat: minimal commit" + assert commit.body == "" + assert commit.author == "John Doe" + assert commit.author_email == "john@example.com" + assert commit.parents == [] + + +@pytest.mark.parametrize( + "os_name,committer_date,expected_cmd", + [ + ( + "nt", + "2024-03-20", + 'cmd /v /c "set GIT_COMMITTER_DATE=2024-03-20&& git commit -F "temp.txt""', + ), + ( + "posix", + "2024-03-20", + 'GIT_COMMITTER_DATE=2024-03-20 git commit -F "temp.txt"', + ), + ("nt", None, 'git commit -F "temp.txt"'), + ("posix", None, 'git commit -F "temp.txt"'), + ], +) +def test_create_commit_cmd_string(mocker, os_name, committer_date, expected_cmd): + """Test the OS-specific behavior of _create_commit_cmd_string""" + mocker.patch("os.name", os_name) + result = git._create_commit_cmd_string("", committer_date, "temp.txt") + assert result == expected_cmd + + +def test_get_default_branch_success(mocker: MockFixture): + mocker.patch( + "commitizen.cmd.run", return_value=FakeCommand(out="refs/remotes/origin/main\n") + ) + assert git.get_default_branch() == "refs/remotes/origin/main" + + +def test_get_default_branch_error(mocker: MockFixture): + mocker.patch( + "commitizen.cmd.run", + return_value=FakeCommand( + err="fatal: ref refs/remotes/origin/HEAD is not a symbolic ref", + return_code=1, + ), + ) + with pytest.raises(GitCommandError): + git.get_default_branch() diff --git a/tests/test_incremental_build.py b/tests/test_incremental_build.py new file mode 100644 index 0000000000..13f123a651 --- /dev/null +++ b/tests/test_incremental_build.py @@ -0,0 +1,606 @@ +"""Tests for the incremental_build function in commitizen.changelog module.""" + +from commitizen.changelog import Metadata, incremental_build + + +class TestIncrementalBuild: + """Test cases for the incremental_build function.""" + + def test_basic_replacement_of_unreleased_section(self): + """Test basic functionality of replacing unreleased section with new content.""" + lines = [ + "# Changelog", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + metadata = Metadata( + unreleased_start=2, + unreleased_end=6, + latest_version="1.0.0", + latest_version_position=7, + ) + + new_content = "## Unreleased\n\n### Added\n- Another new feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- Another new feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + assert result == expected + + def test_replacement_when_latest_version_position_is_none(self): + """Test replacement when latest_version_position is None (append to end).""" + lines = [ + "# Changelog", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + ] + + metadata = Metadata( + unreleased_start=2, + unreleased_end=5, + latest_version=None, + latest_version_position=None, + ) + + new_content = "## Unreleased\n\n### Added\n- Another new feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- Another new feature", + ] + + assert result == expected + + def test_replacement_when_latest_version_position_after_unreleased_end(self): + """Test replacement when latest_version_position is after unreleased_end.""" + lines = [ + "# Changelog", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + metadata = Metadata( + unreleased_start=2, + unreleased_end=5, + latest_version="1.0.0", + latest_version_position=7, + ) + + new_content = "## Unreleased\n\n### Added\n- Another new feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "", + "## Unreleased\n\n### Added\n- Another new feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + assert result == expected + + def test_replacement_when_latest_version_position_before_unreleased_end(self): + """Test replacement when latest_version_position is before unreleased_end.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + ] + + metadata = Metadata( + unreleased_start=7, + unreleased_end=10, + latest_version="1.0.0", + latest_version_position=2, + ) + + new_content = "## Unreleased\n\n### Added\n- Another new feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- Another new feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + "", + "- New feature", + ] + + assert result == expected + + def test_no_unreleased_section_append_to_end(self): + """Test appending new content when no unreleased section exists.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + metadata = Metadata( + unreleased_start=None, + unreleased_end=None, + latest_version=None, + latest_version_position=None, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + "\n", + "## Unreleased\n\n### Added\n- New feature", + ] + + assert result == expected + + def test_no_unreleased_section_with_latest_version_position(self): + """Test inserting new content at latest_version_position when no unreleased section.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + metadata = Metadata( + unreleased_start=None, + unreleased_end=None, + latest_version="1.0.0", + latest_version_position=2, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- New feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + assert result == expected + + def test_empty_lines_list(self): + """Test behavior with empty lines list.""" + lines = [] + + metadata = Metadata( + unreleased_start=None, + unreleased_end=None, + latest_version=None, + latest_version_position=None, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "## Unreleased\n\n### Added\n- New feature", + ] + + assert result == expected + + def test_single_line_unreleased_section(self): + """Test replacement of single line unreleased section.""" + lines = [ + "# Changelog", + "## Unreleased", + "## 1.0.0 (2023-01-01)", + ] + + metadata = Metadata( + unreleased_start=1, + unreleased_end=1, + latest_version="1.0.0", + latest_version_position=2, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + ] + + assert result == expected + + def test_unreleased_section_at_end_of_file(self): + """Test replacement when unreleased section is at the end of file.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + ] + + metadata = Metadata( + unreleased_start=7, + unreleased_end=10, + latest_version="1.0.0", + latest_version_position=2, + ) + + new_content = "## Unreleased\n\n### Added\n- Another new feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- Another new feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + "", + "- New feature", + ] + + assert result == expected + + def test_blank_line_handling_when_appending(self): + """Test that blank line is added when appending to non-empty content.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + ] + + metadata = Metadata( + unreleased_start=None, + unreleased_end=None, + latest_version=None, + latest_version_position=None, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "\n", + "## Unreleased\n\n### Added\n- New feature", + ] + + assert result == expected + + def test_no_blank_line_when_content_ends_with_blank_line(self): + """Test that no extra blank line is added when content already ends with blank line.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + ] + + metadata = Metadata( + unreleased_start=None, + unreleased_end=None, + latest_version=None, + latest_version_position=None, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "## Unreleased\n\n### Added\n- New feature", + ] + + assert result == expected + + def test_empty_new_content(self): + """Test behavior with empty new content.""" + lines = [ + "# Changelog", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + "", + "## 1.0.0 (2023-01-01)", + ] + + metadata = Metadata( + unreleased_start=2, + unreleased_end=5, + latest_version="1.0.0", + latest_version_position=7, + ) + + new_content = "" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "", + "", + "\n", + "## 1.0.0 (2023-01-01)", + ] + + assert result == expected + + def test_multiline_new_content(self): + """Test behavior with multiline new content.""" + lines = [ + "# Changelog", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + "", + "## 1.0.0 (2023-01-01)", + ] + + metadata = Metadata( + unreleased_start=2, + unreleased_end=5, + latest_version="1.0.0", + latest_version_position=7, + ) + + new_content = "## Unreleased\n\n### Added\n- Feature 1\n- Feature 2\n\n### Fixed\n- Bug fix" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "", + "## Unreleased\n\n### Added\n- Feature 1\n- Feature 2\n\n### Fixed\n- Bug fix", + "\n", + "## 1.0.0 (2023-01-01)", + ] + + assert result == expected + + def test_metadata_with_none_values(self): + """Test behavior when metadata has None values for unreleased positions.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + metadata = Metadata( + unreleased_start=None, + unreleased_end=None, + latest_version="1.0.0", + latest_version_position=2, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- New feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + assert result == expected + + def test_skip_behavior_during_unreleased_section(self): + """Test that lines are properly skipped during unreleased section processing.""" + lines = [ + "# Changelog", + "", + "## Unreleased", + "", + "### Added", + "- Old feature 1", + "- Old feature 2", + "", + "### Fixed", + "- Old bug fix", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + metadata = Metadata( + unreleased_start=2, + unreleased_end=9, + latest_version="1.0.0", + latest_version_position=11, + ) + + new_content = "## Unreleased\n\n### Added\n- New feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "", + "## Unreleased\n\n### Added\n- New feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + ] + + assert result == expected + + def test_latest_version_position_at_unreleased_end(self): + """Test behavior when latest_version_position equals unreleased_end.""" + lines = [ + "# Changelog", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + "", + "## 1.0.0 (2023-01-01)", + ] + + metadata = Metadata( + unreleased_start=2, + unreleased_end=5, + latest_version="1.0.0", + latest_version_position=5, + ) + + new_content = "## Unreleased\n\n### Added\n- Another new feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- Another new feature", + "\n", + "- New feature", + "", + "## 1.0.0 (2023-01-01)", + ] + + assert result == expected + + def test_latest_version_position_before_unreleased_start(self): + """Test behavior when latest_version_position is before unreleased_start.""" + lines = [ + "# Changelog", + "", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + "", + "## Unreleased", + "", + "### Added", + "- New feature", + ] + + metadata = Metadata( + unreleased_start=7, + unreleased_end=9, + latest_version="1.0.0", + latest_version_position=2, + ) + + new_content = "## Unreleased\n\n### Added\n- Another new feature" + + result = incremental_build(new_content, lines, metadata) + + expected = [ + "# Changelog", + "", + "## Unreleased\n\n### Added\n- Another new feature", + "\n", + "## 1.0.0 (2023-01-01)", + "", + "### Fixed", + "- Bug fix", + "", + "### Added", + "- New feature", + ] + + assert result == expected diff --git a/tests/test_project_info.py b/tests/test_project_info.py new file mode 100644 index 0000000000..d30a743e58 --- /dev/null +++ b/tests/test_project_info.py @@ -0,0 +1,90 @@ +"""Tests for project_info module.""" + +from __future__ import annotations + +from pathlib import Path + +import pytest + +from commitizen import project_info + + +def _create_project_files(files: dict[str, str | None]) -> None: + for file_path, content in files.items(): + path = Path(file_path) + if content is None: + path.touch() + else: + path.write_text(content) + + +@pytest.mark.parametrize( + "which_return, expected", + [ + ("/usr/local/bin/pre-commit", True), + (None, False), + ("", False), + ], +) +def test_is_pre_commit_installed(mocker, which_return, expected): + mocker.patch("shutil.which", return_value=which_return) + assert project_info.is_pre_commit_installed() is expected + + +@pytest.mark.parametrize( + "files, expected", + [ + ( + {"pyproject.toml": '[tool.poetry]\nname = "test"\nversion = "0.1.0"'}, + "poetry", + ), + ({"pyproject.toml": "", "uv.lock": ""}, "uv"), + ( + {"pyproject.toml": '[tool.commitizen]\nversion = "0.1.0"'}, + "pep621", + ), + ({"setup.py": ""}, "pep621"), + ({"Cargo.toml": ""}, "cargo"), + ({"package.json": ""}, "npm"), + ({"composer.json": ""}, "composer"), + ({}, "commitizen"), + ( + { + "pyproject.toml": "", + "Cargo.toml": "", + "package.json": "", + "composer.json": "", + }, + "pep621", + ), + ], +) +def test_get_default_version_provider(chdir, files, expected): + _create_project_files(files) + assert project_info.get_default_version_provider() == expected + + +@pytest.mark.parametrize( + "files, expected", + [ + ({"pyproject.toml": ""}, "pyproject.toml"), + ({}, ".cz.toml"), + ], +) +def test_get_default_config_filename(chdir, files, expected): + _create_project_files(files) + assert project_info.get_default_config_filename() == expected + + +@pytest.mark.parametrize( + "files, expected", + [ + ({"pyproject.toml": ""}, "pep440"), + ({"setup.py": ""}, "pep440"), + ({"package.json": ""}, "semver"), + ({}, "semver"), + ], +) +def test_get_default_version_scheme(chdir, files, expected): + _create_project_files(files) + assert project_info.get_default_version_scheme() == expected diff --git a/tests/test_version_scheme_pep440.py b/tests/test_version_scheme_pep440.py index 6b1f621cb8..0ce4f81545 100644 --- a/tests/test_version_scheme_pep440.py +++ b/tests/test_version_scheme_pep440.py @@ -1,258 +1,1356 @@ -import itertools -import random - import pytest from commitizen.version_schemes import Pep440, VersionProtocol - -simple_flow = [ - (("0.1.0", "PATCH", None, 0, None), "0.1.1"), - (("0.1.0", "PATCH", None, 0, 1), "0.1.1.dev1"), - (("0.1.1", "MINOR", None, 0, None), "0.2.0"), - (("0.2.0", "MINOR", None, 0, None), "0.3.0"), - (("0.2.0", "MINOR", None, 0, 1), "0.3.0.dev1"), - (("0.3.0", "PATCH", None, 0, None), "0.3.1"), - (("0.3.0", "PATCH", "alpha", 0, None), "0.3.1a0"), - (("0.3.1a0", None, "alpha", 0, None), "0.3.1a1"), - (("0.3.0", "PATCH", "alpha", 1, None), "0.3.1a1"), - (("0.3.1a0", None, "alpha", 1, None), "0.3.1a1"), - (("0.3.1a0", None, None, 0, None), "0.3.1"), - (("0.3.1", "PATCH", None, 0, None), "0.3.2"), - (("0.4.2", "MAJOR", "alpha", 0, None), "1.0.0a0"), - (("1.0.0a0", None, "alpha", 0, None), "1.0.0a1"), - (("1.0.0a1", None, "alpha", 0, None), "1.0.0a2"), - (("1.0.0a1", None, "alpha", 0, 1), "1.0.0a2.dev1"), - (("1.0.0a2.dev0", None, "alpha", 0, 1), "1.0.0a3.dev1"), - (("1.0.0a2.dev0", None, "alpha", 0, 0), "1.0.0a3.dev0"), - (("1.0.0a1", None, "beta", 0, None), "1.0.0b0"), - (("1.0.0b0", None, "beta", 0, None), "1.0.0b1"), - (("1.0.0b1", None, "rc", 0, None), "1.0.0rc0"), - (("1.0.0rc0", None, "rc", 0, None), "1.0.0rc1"), - (("1.0.0rc0", None, "rc", 0, 1), "1.0.0rc1.dev1"), - (("1.0.0rc0", "PATCH", None, 0, None), "1.0.0"), - (("1.0.0a3.dev0", None, "beta", 0, None), "1.0.0b0"), - (("1.0.0", "PATCH", None, 0, None), "1.0.1"), - (("1.0.1", "PATCH", None, 0, None), "1.0.2"), - (("1.0.2", "MINOR", None, 0, None), "1.1.0"), - (("1.1.0", "MINOR", None, 0, None), "1.2.0"), - (("1.2.0", "PATCH", None, 0, None), "1.2.1"), - (("1.2.1", "MAJOR", None, 0, None), "2.0.0"), -] - -local_versions = [ - (("4.5.0+0.1.0", "PATCH", None, 0, None), "4.5.0+0.1.1"), - (("4.5.0+0.1.1", "MINOR", None, 0, None), "4.5.0+0.2.0"), - (("4.5.0+0.2.0", "MAJOR", None, 0, None), "4.5.0+1.0.0"), -] - -# never bump backwards on pre-releases -linear_prerelease_cases = [ - (("0.1.1b1", None, "alpha", 0, None), "0.1.1b2"), - (("0.1.1rc0", None, "alpha", 0, None), "0.1.1rc1"), - (("0.1.1rc0", None, "beta", 0, None), "0.1.1rc1"), -] - -weird_cases = [ - (("1.1", "PATCH", None, 0, None), "1.1.1"), - (("1", "MINOR", None, 0, None), "1.1.0"), - (("1", "MAJOR", None, 0, None), "2.0.0"), - (("1a0", None, "alpha", 0, None), "1.0.0a1"), - (("1a0", None, "alpha", 1, None), "1.0.0a1"), - (("1", None, "beta", 0, None), "1.0.0b0"), - (("1", None, "beta", 1, None), "1.0.0b1"), - (("1beta", None, "beta", 0, None), "1.0.0b1"), - (("1.0.0alpha1", None, "alpha", 0, None), "1.0.0a2"), - (("1", None, "rc", 0, None), "1.0.0rc0"), - (("1.0.0rc1+e20d7b57f3eb", "PATCH", None, 0, None), "1.0.0"), -] - -# test driven development -tdd_cases = [ - (("0.1.1", "PATCH", None, 0, None), "0.1.2"), - (("0.1.1", "MINOR", None, 0, None), "0.2.0"), - (("2.1.1", "MAJOR", None, 0, None), "3.0.0"), - (("0.9.0", "PATCH", "alpha", 0, None), "0.9.1a0"), - (("0.9.0", "MINOR", "alpha", 0, None), "0.10.0a0"), - (("0.9.0", "MAJOR", "alpha", 0, None), "1.0.0a0"), - (("0.9.0", "MAJOR", "alpha", 1, None), "1.0.0a1"), - (("1.0.0a2", None, "beta", 0, None), "1.0.0b0"), - (("1.0.0a2", None, "beta", 1, None), "1.0.0b1"), - (("1.0.0beta1", None, "rc", 0, None), "1.0.0rc0"), - (("1.0.0rc1", None, "rc", 0, None), "1.0.0rc2"), -] - -# additional pre-release tests run through various release scenarios -prerelease_cases = [ - # - (("3.3.3", "PATCH", "alpha", 0, None), "3.3.4a0"), - (("3.3.4a0", "PATCH", "alpha", 0, None), "3.3.4a1"), - (("3.3.4a1", "MINOR", "alpha", 0, None), "3.4.0a0"), - (("3.4.0a0", "PATCH", "alpha", 0, None), "3.4.0a1"), - (("3.4.0a1", "MINOR", "alpha", 0, None), "3.4.0a2"), - (("3.4.0a2", "MAJOR", "alpha", 0, None), "4.0.0a0"), - (("4.0.0a0", "PATCH", "alpha", 0, None), "4.0.0a1"), - (("4.0.0a1", "MINOR", "alpha", 0, None), "4.0.0a2"), - (("4.0.0a2", "MAJOR", "alpha", 0, None), "4.0.0a3"), - # - (("1.0.0", "PATCH", "alpha", 0, None), "1.0.1a0"), - (("1.0.1a0", "PATCH", "alpha", 0, None), "1.0.1a1"), - (("1.0.1a1", "MINOR", "alpha", 0, None), "1.1.0a0"), - (("1.1.0a0", "PATCH", "alpha", 0, None), "1.1.0a1"), - (("1.1.0a1", "MINOR", "alpha", 0, None), "1.1.0a2"), - (("1.1.0a2", "MAJOR", "alpha", 0, None), "2.0.0a0"), - # - (("1.0.0", "MINOR", "alpha", 0, None), "1.1.0a0"), - (("1.1.0a0", "PATCH", "alpha", 0, None), "1.1.0a1"), - (("1.1.0a1", "MINOR", "alpha", 0, None), "1.1.0a2"), - (("1.1.0a2", "PATCH", "alpha", 0, None), "1.1.0a3"), - (("1.1.0a3", "MAJOR", "alpha", 0, None), "2.0.0a0"), - # - (("1.0.0", "MAJOR", "alpha", 0, None), "2.0.0a0"), - (("2.0.0a0", "MINOR", "alpha", 0, None), "2.0.0a1"), - (("2.0.0a1", "PATCH", "alpha", 0, None), "2.0.0a2"), - (("2.0.0a2", "MAJOR", "alpha", 0, None), "2.0.0a3"), - (("2.0.0a3", "MINOR", "alpha", 0, None), "2.0.0a4"), - (("2.0.0a4", "PATCH", "alpha", 0, None), "2.0.0a5"), - (("2.0.0a5", "MAJOR", "alpha", 0, None), "2.0.0a6"), - # - (("2.0.0b0", "MINOR", "alpha", 0, None), "2.0.0b1"), - (("2.0.0b0", "PATCH", "alpha", 0, None), "2.0.0b1"), - # - (("1.0.1a0", "PATCH", None, 0, None), "1.0.1"), - (("1.0.1a0", "MINOR", None, 0, None), "1.1.0"), - (("1.0.1a0", "MAJOR", None, 0, None), "2.0.0"), - # - (("1.1.0a0", "PATCH", None, 0, None), "1.1.0"), - (("1.1.0a0", "MINOR", None, 0, None), "1.1.0"), - (("1.1.0a0", "MAJOR", None, 0, None), "2.0.0"), - # - (("2.0.0a0", "MINOR", None, 0, None), "2.0.0"), - (("2.0.0a0", "MAJOR", None, 0, None), "2.0.0"), - (("2.0.0a0", "PATCH", None, 0, None), "2.0.0"), - # - (("3.0.0a1", None, None, 0, None), "3.0.0"), - (("3.0.0b1", None, None, 0, None), "3.0.0"), - (("3.0.0rc1", None, None, 0, None), "3.0.0"), - # - (("3.1.4", None, "alpha", 0, None), "3.1.4a0"), - (("3.1.4", None, "beta", 0, None), "3.1.4b0"), - (("3.1.4", None, "rc", 0, None), "3.1.4rc0"), - # - (("3.1.4", None, "alpha", 0, None), "3.1.4a0"), - (("3.1.4a0", "PATCH", "alpha", 0, None), "3.1.4a1"), # UNEXPECTED! - (("3.1.4a0", "MINOR", "alpha", 0, None), "3.2.0a0"), - (("3.1.4a0", "MAJOR", "alpha", 0, None), "4.0.0a0"), -] - -excact_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" - (("1.0.0a1", "PATCH", "beta", 0, None), "1.0.1b0"), - # with exact_increment=False: "1.0.0b1" - (("1.0.0b0", "PATCH", "beta", 0, None), "1.0.1b0"), - # with exact_increment=False: "1.0.0rc0" - (("1.0.0b1", "PATCH", "rc", 0, None), "1.0.1rc0"), - # with exact_increment=False: "1.0.0-rc1" - (("1.0.0rc0", "PATCH", "rc", 0, None), "1.0.1rc0"), - # with exact_increment=False: "1.0.0rc1-dev1" - (("1.0.0rc0", "PATCH", "rc", 0, 1), "1.0.1rc0.dev1"), - # with exact_increment=False: "1.0.0b0" - (("1.0.0a1", "MINOR", "beta", 0, None), "1.1.0b0"), - # with exact_increment=False: "1.0.0b1" - (("1.0.0b0", "MINOR", "beta", 0, None), "1.1.0b0"), - # with exact_increment=False: "1.0.0b1" - (("1.0.0b0", "MINOR", "alpha", 0, None), "1.1.0a0"), - # with exact_increment=False: "1.0.0rc0" - (("1.0.0b1", "MINOR", "rc", 0, None), "1.1.0rc0"), - # with exact_increment=False: "1.0.0rc1" - (("1.0.0rc0", "MINOR", "rc", 0, None), "1.1.0rc0"), - # with exact_increment=False: "1.0.0rc1-dev1" - (("1.0.0rc0", "MINOR", "rc", 0, 1), "1.1.0rc0.dev1"), - # with exact_increment=False: "2.0.0" - (("2.0.0b0", "MAJOR", None, 0, None), "3.0.0"), - # with exact_increment=False: "2.0.0" - (("2.0.0b0", "MINOR", None, 0, None), "2.1.0"), - # with exact_increment=False: "2.0.0" - (("2.0.0b0", "PATCH", None, 0, None), "2.0.1"), - # same with exact_increment=False - (("2.0.0b0", "MAJOR", "alpha", 0, None), "3.0.0a0"), - # with exact_increment=False: "2.0.0b1" - (("2.0.0b0", "MINOR", "alpha", 0, None), "2.1.0a0"), - # with exact_increment=False: "2.0.0b1" - (("2.0.0b0", "PATCH", "alpha", 0, None), "2.0.1a0"), -] +from tests.utils import VersionSchemeTestArgs @pytest.mark.parametrize( - "test_input,expected", - itertools.chain( - tdd_cases, - weird_cases, - simple_flow, - linear_prerelease_cases, - prerelease_cases, - ), + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.1.2", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="2.1.1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.9.1a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.10.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0b0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2", + increment=None, + prerelease="beta", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0b1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0beta1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0rc0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0rc2", + ), + # weird cases + ( + VersionSchemeTestArgs( + current_version="1.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="1a0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="1a0", + increment=None, + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0b0", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="beta", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0b1", + ), + ( + VersionSchemeTestArgs( + current_version="1beta", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0b1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0alpha1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0a2", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0rc0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc1+e20d7b57f3eb", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.0", + ), + # simple flow + ( + VersionSchemeTestArgs( + current_version="0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=1, + ), + "0.1.1.dev1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.2.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.2.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=1, + ), + "0.3.0.dev1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.3.1a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1a0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.3.1a1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "0.3.1a1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1a0", + increment=None, + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "0.3.1a1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1a0", + increment=None, + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.2", + ), + ( + VersionSchemeTestArgs( + current_version="0.4.2", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0a2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0a2.dev1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2.dev0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0a3.dev1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2.dev0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=0, + ), + "1.0.0a3.dev0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0b0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0b0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0b1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0b1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0rc0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0rc1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0rc1.dev1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a3.dev0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0b0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.2", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.2.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.2.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.2.1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + # linear prerelease cases + ( + VersionSchemeTestArgs( + current_version="0.1.1b1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1b2", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1rc0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1rc1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1rc0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1rc1", + ), + # prerelease cases + ( + VersionSchemeTestArgs( + current_version="3.3.3", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.3.4a0", + ), + ( + VersionSchemeTestArgs( + current_version="3.3.4a0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.3.4a1", + ), + ( + VersionSchemeTestArgs( + current_version="3.3.4a1", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.4.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="3.4.0a0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.4.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="3.4.0a1", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.4.0a2", + ), + ( + VersionSchemeTestArgs( + current_version="3.4.0a2", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "4.0.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="4.0.0a0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "4.0.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="4.0.0a1", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "4.0.0a2", + ), + ( + VersionSchemeTestArgs( + current_version="4.0.0a2", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "4.0.0a3", + ), + # + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1a0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.1a0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1a1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.1a1", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a1", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a2", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a2", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a0", + ), + # + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a1", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a2", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a2", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a3", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a3", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a0", + ), + # + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a1", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a1", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a2", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a2", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a3", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a3", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a4", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a4", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a5", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a5", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0a6", + ), + # + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0b1", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.0b1", + ), + # + ( + VersionSchemeTestArgs( + current_version="1.0.1a0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.1a0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.1a0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + # + ( + VersionSchemeTestArgs( + current_version="1.1.0a0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0a0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + # + ( + VersionSchemeTestArgs( + current_version="2.0.0a0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="2.0.0a0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + # + ( + VersionSchemeTestArgs( + current_version="3.0.0a1", + increment=None, + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="3.0.0b1", + increment=None, + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="3.0.0rc1", + increment=None, + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + # + ( + VersionSchemeTestArgs( + current_version="3.1.4", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.1.4a0", + ), + ( + VersionSchemeTestArgs( + current_version="3.1.4", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "3.1.4b0", + ), + ( + VersionSchemeTestArgs( + current_version="3.1.4", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "3.1.4rc0", + ), + # + ( + VersionSchemeTestArgs( + current_version="3.1.4", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.1.4a0", + ), + ( + VersionSchemeTestArgs( + current_version="3.1.4a0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.1.4a1", + ), + ( + VersionSchemeTestArgs( + current_version="3.1.4a0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.2.0a0", + ), + ( + VersionSchemeTestArgs( + current_version="3.1.4a0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "4.0.0a0", + ), + ], ) -def test_bump_pep440_version(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] +def test_bump_pep440_version(version_args, expected_version): assert ( str( - Pep440(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, + Pep440(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, ) ) - == expected + == expected_version ) -@pytest.mark.parametrize("test_input, expected", excact_cases) -def test_bump_pep440_version_force(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] +@pytest.mark.parametrize( + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + # with exact_increment=False: "1.0.0b0" + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment="PATCH", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1b0", + ), + # with exact_increment=False: "1.0.0b1" + ( + VersionSchemeTestArgs( + current_version="1.0.0b0", + increment="PATCH", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1b0", + ), + # with exact_increment=False: "1.0.0rc0" + ( + VersionSchemeTestArgs( + current_version="1.0.0b1", + increment="PATCH", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1rc0", + ), + # with exact_increment=False: "1.0.0-rc1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="PATCH", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1rc0", + ), + # with exact_increment=False: "1.0.0rc1-dev1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="PATCH", + prerelease="rc", + prerelease_offset=0, + devrelease=1, + ), + "1.0.1rc0.dev1", + ), + # with exact_increment=False: "1.0.0b0" + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment="MINOR", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0b0", + ), + # with exact_increment=False: "1.0.0b1" + ( + VersionSchemeTestArgs( + current_version="1.0.0b0", + increment="MINOR", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0b0", + ), + # with exact_increment=False: "1.0.0b1" + ( + VersionSchemeTestArgs( + current_version="1.0.0b0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0a0", + ), + # with exact_increment=False: "1.0.0rc0" + ( + VersionSchemeTestArgs( + current_version="1.0.0b1", + increment="MINOR", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0rc0", + ), + # with exact_increment=False: "1.0.0rc1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="MINOR", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0rc0", + ), + # with exact_increment=False: "1.0.0rc1-dev1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="MINOR", + prerelease="rc", + prerelease_offset=0, + devrelease=1, + ), + "1.1.0rc0.dev1", + ), + # with exact_increment=False: "2.0.0" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + # with exact_increment=False: "2.0.0" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.1.0", + ), + # with exact_increment=False: "2.0.0" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.1", + ), + # same with exact_increment=False + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.0.0a0", + ), + # with exact_increment=False: "2.0.0b1" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.1.0a0", + ), + # with exact_increment=False: "2.0.0b1" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.1a0", + ), + ], +) +def test_bump_pep440_version_force(version_args, expected_version): assert ( str( - Pep440(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, + Pep440(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, exact_increment=True, ) ) - == expected + == expected_version ) -@pytest.mark.parametrize("test_input,expected", local_versions) -def test_bump_pep440_version_local(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] - is_local_version = True +@pytest.mark.parametrize( + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+0.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.2.0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+1.0.0", + ), + ], +) +def test_bump_pep440_version_local(version_args, expected_version): assert ( str( - Pep440(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, - is_local_version=is_local_version, + Pep440(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, + is_local_version=True, ) ) - == expected + == expected_version ) @@ -263,76 +1361,3 @@ def test_pep440_scheme_property(): def test_pep440_implement_version_protocol(): assert isinstance(Pep440("0.0.1"), VersionProtocol) - - -def test_pep440_sortable(): - test_input = [x[0][0] for x in simple_flow] - test_input.extend([x[1] for x in simple_flow]) - # randomize - random_input = [Pep440(x) for x in random.sample(test_input, len(test_input))] - assert len(random_input) == len(test_input) - sorted_result = [str(x) for x in sorted(random_input)] - assert sorted_result == [ - "0.1.0", - "0.1.0", - "0.1.1.dev1", - "0.1.1", - "0.1.1", - "0.2.0", - "0.2.0", - "0.2.0", - "0.3.0.dev1", - "0.3.0", - "0.3.0", - "0.3.0", - "0.3.0", - "0.3.1a0", - "0.3.1a0", - "0.3.1a0", - "0.3.1a0", - "0.3.1a1", - "0.3.1a1", - "0.3.1a1", - "0.3.1", - "0.3.1", - "0.3.1", - "0.3.2", - "0.4.2", - "1.0.0a0", - "1.0.0a0", - "1.0.0a1", - "1.0.0a1", - "1.0.0a1", - "1.0.0a1", - "1.0.0a2.dev0", - "1.0.0a2.dev0", - "1.0.0a2.dev1", - "1.0.0a2", - "1.0.0a3.dev0", - "1.0.0a3.dev0", - "1.0.0a3.dev1", - "1.0.0b0", - "1.0.0b0", - "1.0.0b0", - "1.0.0b1", - "1.0.0b1", - "1.0.0rc0", - "1.0.0rc0", - "1.0.0rc0", - "1.0.0rc0", - "1.0.0rc1.dev1", - "1.0.0rc1", - "1.0.0", - "1.0.0", - "1.0.1", - "1.0.1", - "1.0.2", - "1.0.2", - "1.1.0", - "1.1.0", - "1.2.0", - "1.2.0", - "1.2.1", - "1.2.1", - "2.0.0", - ] diff --git a/tests/test_version_scheme_semver.py b/tests/test_version_scheme_semver.py index 71d5e5876c..8a163d4f6b 100644 --- a/tests/test_version_scheme_semver.py +++ b/tests/test_version_scheme_semver.py @@ -1,189 +1,882 @@ -import itertools -import random +from __future__ import annotations import pytest from commitizen.version_schemes import SemVer, VersionProtocol - -simple_flow = [ - (("0.1.0", "PATCH", None, 0, None), "0.1.1"), - (("0.1.0", "PATCH", None, 0, 1), "0.1.1-dev1"), - (("0.1.1", "MINOR", None, 0, None), "0.2.0"), - (("0.2.0", "MINOR", None, 0, None), "0.3.0"), - (("0.2.0", "MINOR", None, 0, 1), "0.3.0-dev1"), - (("0.3.0", "PATCH", None, 0, None), "0.3.1"), - (("0.3.0", "PATCH", "alpha", 0, None), "0.3.1-a0"), - (("0.3.1a0", None, "alpha", 0, None), "0.3.1-a1"), - (("0.3.0", "PATCH", "alpha", 1, None), "0.3.1-a1"), - (("0.3.1a0", None, "alpha", 1, None), "0.3.1-a1"), - (("0.3.1a0", None, None, 0, None), "0.3.1"), - (("0.3.1", "PATCH", None, 0, None), "0.3.2"), - (("0.4.2", "MAJOR", "alpha", 0, None), "1.0.0-a0"), - (("1.0.0a0", None, "alpha", 0, None), "1.0.0-a1"), - (("1.0.0a1", None, "alpha", 0, None), "1.0.0-a2"), - (("1.0.0a1", None, "alpha", 0, 1), "1.0.0-a2-dev1"), - (("1.0.0a2.dev0", None, "alpha", 0, 1), "1.0.0-a3-dev1"), - (("1.0.0a2.dev0", None, "alpha", 0, 0), "1.0.0-a3-dev0"), - (("1.0.0a1", None, "beta", 0, None), "1.0.0-b0"), - (("1.0.0b0", None, "beta", 0, None), "1.0.0-b1"), - (("1.0.0b1", None, "rc", 0, None), "1.0.0-rc0"), - (("1.0.0rc0", None, "rc", 0, None), "1.0.0-rc1"), - (("1.0.0rc0", None, "rc", 0, 1), "1.0.0-rc1-dev1"), - (("1.0.0rc0", "PATCH", None, 0, None), "1.0.0"), - (("1.0.0a3.dev0", None, "beta", 0, None), "1.0.0-b0"), - (("1.0.0", "PATCH", None, 0, None), "1.0.1"), - (("1.0.1", "PATCH", None, 0, None), "1.0.2"), - (("1.0.2", "MINOR", None, 0, None), "1.1.0"), - (("1.1.0", "MINOR", None, 0, None), "1.2.0"), - (("1.2.0", "PATCH", None, 0, None), "1.2.1"), - (("1.2.1", "MAJOR", None, 0, None), "2.0.0"), -] - -local_versions = [ - (("4.5.0+0.1.0", "PATCH", None, 0, None), "4.5.0+0.1.1"), - (("4.5.0+0.1.1", "MINOR", None, 0, None), "4.5.0+0.2.0"), - (("4.5.0+0.2.0", "MAJOR", None, 0, None), "4.5.0+1.0.0"), -] - -# never bump backwards on pre-releases -linear_prerelease_cases = [ - (("0.1.1b1", None, "alpha", 0, None), "0.1.1-b2"), - (("0.1.1rc0", None, "alpha", 0, None), "0.1.1-rc1"), - (("0.1.1rc0", None, "beta", 0, None), "0.1.1-rc1"), -] - -weird_cases = [ - (("1.1", "PATCH", None, 0, None), "1.1.1"), - (("1", "MINOR", None, 0, None), "1.1.0"), - (("1", "MAJOR", None, 0, None), "2.0.0"), - (("1a0", None, "alpha", 0, None), "1.0.0-a1"), - (("1a0", None, "alpha", 1, None), "1.0.0-a1"), - (("1", None, "beta", 0, None), "1.0.0-b0"), - (("1", None, "beta", 1, None), "1.0.0-b1"), - (("1beta", None, "beta", 0, None), "1.0.0-b1"), - (("1.0.0alpha1", None, "alpha", 0, None), "1.0.0-a2"), - (("1", None, "rc", 0, None), "1.0.0-rc0"), - (("1.0.0rc1+e20d7b57f3eb", "PATCH", None, 0, None), "1.0.0"), -] - -# test driven development -tdd_cases = [ - (("0.1.1", "PATCH", None, 0, None), "0.1.2"), - (("0.1.1", "MINOR", None, 0, None), "0.2.0"), - (("2.1.1", "MAJOR", None, 0, None), "3.0.0"), - (("0.9.0", "PATCH", "alpha", 0, None), "0.9.1-a0"), - (("0.9.0", "MINOR", "alpha", 0, None), "0.10.0-a0"), - (("0.9.0", "MAJOR", "alpha", 0, None), "1.0.0-a0"), - (("0.9.0", "MAJOR", "alpha", 1, None), "1.0.0-a1"), - (("1.0.0a2", None, "beta", 0, None), "1.0.0-b0"), - (("1.0.0a2", None, "beta", 1, None), "1.0.0-b1"), - (("1.0.0beta1", None, "rc", 0, None), "1.0.0-rc0"), - (("1.0.0rc1", None, "rc", 0, None), "1.0.0-rc2"), - (("1.0.0-a0", None, "rc", 0, None), "1.0.0-rc0"), - (("1.0.0-alpha1", None, "alpha", 0, None), "1.0.0-a2"), -] - -excact_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" - (("1.0.0a1", "PATCH", "beta", 0, None), "1.0.1-b0"), - # with exact_increment=False: "1.0.0-b1" - (("1.0.0b0", "PATCH", "beta", 0, None), "1.0.1-b0"), - # with exact_increment=False: "1.0.0-rc0" - (("1.0.0b1", "PATCH", "rc", 0, None), "1.0.1-rc0"), - # with exact_increment=False: "1.0.0-rc1" - (("1.0.0rc0", "PATCH", "rc", 0, None), "1.0.1-rc0"), - # with exact_increment=False: "1.0.0-rc1-dev1" - (("1.0.0rc0", "PATCH", "rc", 0, 1), "1.0.1-rc0-dev1"), - # with exact_increment=False: "1.0.0-b0" - (("1.0.0a1", "MINOR", "beta", 0, None), "1.1.0-b0"), - # with exact_increment=False: "1.0.0-b1" - (("1.0.0b0", "MINOR", "beta", 0, None), "1.1.0-b0"), - # with exact_increment=False: "1.0.0-rc0" - (("1.0.0b1", "MINOR", "rc", 0, None), "1.1.0-rc0"), - # with exact_increment=False: "1.0.0-rc1" - (("1.0.0rc0", "MINOR", "rc", 0, None), "1.1.0-rc0"), - # with exact_increment=False: "1.0.0-rc1-dev1" - (("1.0.0rc0", "MINOR", "rc", 0, 1), "1.1.0-rc0-dev1"), - # with exact_increment=False: "2.0.0" - (("2.0.0b0", "MAJOR", None, 0, None), "3.0.0"), - # with exact_increment=False: "2.0.0" - (("2.0.0b0", "MINOR", None, 0, None), "2.1.0"), - # with exact_increment=False: "2.0.0" - (("2.0.0b0", "PATCH", None, 0, None), "2.0.1"), - # same with exact_increment=False - (("2.0.0b0", "MAJOR", "alpha", 0, None), "3.0.0-a0"), - # with exact_increment=False: "2.0.0b1" - (("2.0.0b0", "MINOR", "alpha", 0, None), "2.1.0-a0"), - # with exact_increment=False: "2.0.0b1" - (("2.0.0b0", "PATCH", "alpha", 0, None), "2.0.1-a0"), -] +from tests.utils import VersionSchemeTestArgs @pytest.mark.parametrize( - "test_input, expected", - itertools.chain(tdd_cases, weird_cases, simple_flow, linear_prerelease_cases), + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.1.2", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="2.1.1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.9.1-a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.10.0-a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-a1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-b0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2", + increment=None, + prerelease="beta", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-b1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0beta1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-a0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-a2", + ), + # weird cases + ( + VersionSchemeTestArgs( + current_version="1.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="1a0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-a1", + ), + ( + VersionSchemeTestArgs( + current_version="1a0", + increment=None, + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-a1", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-b0", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="beta", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-b1", + ), + ( + VersionSchemeTestArgs( + current_version="1beta", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-b1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0alpha1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-a2", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc1+e20d7b57f3eb", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.0", + ), + # simple flow + ( + VersionSchemeTestArgs( + current_version="0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=1, + ), + "0.1.1-dev1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.2.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.2.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=1, + ), + "0.3.0-dev1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.3.1-a0", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1a0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.3.1-a1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "0.3.1-a1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1a0", + increment=None, + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "0.3.1-a1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1a0", + increment=None, + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.2", + ), + ( + VersionSchemeTestArgs( + current_version="0.4.2", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-a0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-a1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-a2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0-a2-dev1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2.dev0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0-a3-dev1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a2.dev0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=0, + ), + "1.0.0-a3-dev0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-b0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0b0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-b1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0b1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0-rc1-dev1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0a3.dev0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-b0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.2", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.2.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.2.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.2.1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + # linear prerelease cases (never bump backwards on pre-releases) + ( + VersionSchemeTestArgs( + current_version="0.1.1b1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1-b2", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1rc0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1-rc1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1rc0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1-rc1", + ), + ], ) -def test_bump_semver_version(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] +def test_bump_semver_version( + version_args: VersionSchemeTestArgs, expected_version: str +): assert ( str( - SemVer(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, + SemVer(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, ) ) - == expected + == expected_version ) -@pytest.mark.parametrize("test_input, expected", excact_cases) -def test_bump_semver_version_force(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] +@pytest.mark.parametrize( + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + # with exact_increment=False: "1.0.0-b0" + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment="PATCH", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1-b0", + ), + # with exact_increment=False: "1.0.0-b1" + ( + VersionSchemeTestArgs( + current_version="1.0.0b0", + increment="PATCH", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1-b0", + ), + # with exact_increment=False: "1.0.0-rc0" + ( + VersionSchemeTestArgs( + current_version="1.0.0b1", + increment="PATCH", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1-rc0", + ), + # with exact_increment=False: "1.0.0-rc1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="PATCH", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.1-rc0", + ), + # with exact_increment=False: "1.0.0-rc1-dev1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="PATCH", + prerelease="rc", + prerelease_offset=0, + devrelease=1, + ), + "1.0.1-rc0-dev1", + ), + # with exact_increment=False: "1.0.0-b0" + ( + VersionSchemeTestArgs( + current_version="1.0.0a1", + increment="MINOR", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0-b0", + ), + # with exact_increment=False: "1.0.0-b1" + ( + VersionSchemeTestArgs( + current_version="1.0.0b0", + increment="MINOR", + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0-b0", + ), + # with exact_increment=False: "1.0.0-rc0" + ( + VersionSchemeTestArgs( + current_version="1.0.0b1", + increment="MINOR", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0-rc0", + ), + # with exact_increment=False: "1.0.0-rc1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="MINOR", + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.1.0-rc0", + ), + # with exact_increment=False: "1.0.0-rc1-dev1" + ( + VersionSchemeTestArgs( + current_version="1.0.0rc0", + increment="MINOR", + prerelease="rc", + prerelease_offset=0, + devrelease=1, + ), + "1.1.0-rc0-dev1", + ), + # with exact_increment=False: "2.0.0" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + # with exact_increment=False: "2.0.0" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.1.0", + ), + # with exact_increment=False: "2.0.0" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.1", + ), + # same with exact_increment=False + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "3.0.0-a0", + ), + # with exact_increment=False: "2.0.0b1" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.1.0-a0", + ), + # with exact_increment=False: "2.0.0b1" + ( + VersionSchemeTestArgs( + current_version="2.0.0b0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "2.0.1-a0", + ), + ], +) +def test_bump_semver_version_force( + version_args: VersionSchemeTestArgs, expected_version: str +): assert ( str( - SemVer(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, + SemVer(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, exact_increment=True, ) ) - == expected + == expected_version ) -@pytest.mark.parametrize("test_input,expected", local_versions) -def test_bump_semver_version_local(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] - is_local_version = True +@pytest.mark.parametrize( + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+0.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.2.0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+1.0.0", + ), + ], +) +def test_bump_semver_version_local( + version_args: VersionSchemeTestArgs, expected_version: str +): assert ( str( - SemVer(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, - is_local_version=is_local_version, + SemVer(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, + is_local_version=True, ) ) - == expected + == expected_version ) @@ -194,76 +887,3 @@ def test_semver_scheme_property(): def test_semver_implement_version_protocol(): assert isinstance(SemVer("0.0.1"), VersionProtocol) - - -def test_semver_sortable(): - test_input = [x[0][0] for x in simple_flow] - test_input.extend([x[1] for x in simple_flow]) - # randomize - random_input = [SemVer(x) for x in random.sample(test_input, len(test_input))] - assert len(random_input) == len(test_input) - sorted_result = [str(x) for x in sorted(random_input)] - assert sorted_result == [ - "0.1.0", - "0.1.0", - "0.1.1-dev1", - "0.1.1", - "0.1.1", - "0.2.0", - "0.2.0", - "0.2.0", - "0.3.0-dev1", - "0.3.0", - "0.3.0", - "0.3.0", - "0.3.0", - "0.3.1-a0", - "0.3.1-a0", - "0.3.1-a0", - "0.3.1-a0", - "0.3.1-a1", - "0.3.1-a1", - "0.3.1-a1", - "0.3.1", - "0.3.1", - "0.3.1", - "0.3.2", - "0.4.2", - "1.0.0-a0", - "1.0.0-a0", - "1.0.0-a1", - "1.0.0-a1", - "1.0.0-a1", - "1.0.0-a1", - "1.0.0-a2-dev0", - "1.0.0-a2-dev0", - "1.0.0-a2-dev1", - "1.0.0-a2", - "1.0.0-a3-dev0", - "1.0.0-a3-dev0", - "1.0.0-a3-dev1", - "1.0.0-b0", - "1.0.0-b0", - "1.0.0-b0", - "1.0.0-b1", - "1.0.0-b1", - "1.0.0-rc0", - "1.0.0-rc0", - "1.0.0-rc0", - "1.0.0-rc0", - "1.0.0-rc1-dev1", - "1.0.0-rc1", - "1.0.0", - "1.0.0", - "1.0.1", - "1.0.1", - "1.0.2", - "1.0.2", - "1.1.0", - "1.1.0", - "1.2.0", - "1.2.0", - "1.2.1", - "1.2.1", - "2.0.0", - ] diff --git a/tests/test_version_scheme_semver2.py b/tests/test_version_scheme_semver2.py index d18a058a7c..4a35e6470a 100644 --- a/tests/test_version_scheme_semver2.py +++ b/tests/test_version_scheme_semver2.py @@ -1,131 +1,664 @@ -import itertools -import random +from __future__ import annotations import pytest from commitizen.version_schemes import SemVer2, VersionProtocol - -simple_flow = [ - (("0.1.0", "PATCH", None, 0, None), "0.1.1"), - (("0.1.0", "PATCH", None, 0, 1), "0.1.1-dev.1"), - (("0.1.1", "MINOR", None, 0, None), "0.2.0"), - (("0.2.0", "MINOR", None, 0, None), "0.3.0"), - (("0.2.0", "MINOR", None, 0, 1), "0.3.0-dev.1"), - (("0.3.0", "PATCH", None, 0, None), "0.3.1"), - (("0.3.0", "PATCH", "alpha", 0, None), "0.3.1-alpha.0"), - (("0.3.1-alpha.0", None, "alpha", 0, None), "0.3.1-alpha.1"), - (("0.3.0", "PATCH", "alpha", 1, None), "0.3.1-alpha.1"), - (("0.3.1-alpha.0", None, "alpha", 1, None), "0.3.1-alpha.1"), - (("0.3.1-alpha.0", None, None, 0, None), "0.3.1"), - (("0.3.1", "PATCH", None, 0, None), "0.3.2"), - (("0.4.2", "MAJOR", "alpha", 0, None), "1.0.0-alpha.0"), - (("1.0.0-alpha.0", None, "alpha", 0, None), "1.0.0-alpha.1"), - (("1.0.0-alpha.1", None, "alpha", 0, None), "1.0.0-alpha.2"), - (("1.0.0-alpha.1", None, "alpha", 0, 1), "1.0.0-alpha.2.dev.1"), - (("1.0.0-alpha.2.dev.0", None, "alpha", 0, 1), "1.0.0-alpha.3.dev.1"), - (("1.0.0-alpha.2.dev.0", None, "alpha", 0, 0), "1.0.0-alpha.3.dev.0"), - (("1.0.0-alpha.1", None, "beta", 0, None), "1.0.0-beta.0"), - (("1.0.0-beta.0", None, "beta", 0, None), "1.0.0-beta.1"), - (("1.0.0-beta.1", None, "rc", 0, None), "1.0.0-rc.0"), - (("1.0.0-rc.0", None, "rc", 0, None), "1.0.0-rc.1"), - (("1.0.0-rc.0", None, "rc", 0, 1), "1.0.0-rc.1.dev.1"), - (("1.0.0-rc.0", "PATCH", None, 0, None), "1.0.0"), - (("1.0.0-alpha.3.dev.0", None, "beta", 0, None), "1.0.0-beta.0"), - (("1.0.0", "PATCH", None, 0, None), "1.0.1"), - (("1.0.1", "PATCH", None, 0, None), "1.0.2"), - (("1.0.2", "MINOR", None, 0, None), "1.1.0"), - (("1.1.0", "MINOR", None, 0, None), "1.2.0"), - (("1.2.0", "PATCH", None, 0, None), "1.2.1"), - (("1.2.1", "MAJOR", None, 0, None), "2.0.0"), -] - -local_versions = [ - (("4.5.0+0.1.0", "PATCH", None, 0, None), "4.5.0+0.1.1"), - (("4.5.0+0.1.1", "MINOR", None, 0, None), "4.5.0+0.2.0"), - (("4.5.0+0.2.0", "MAJOR", None, 0, None), "4.5.0+1.0.0"), -] - -# never bump backwards on pre-releases -linear_prerelease_cases = [ - (("0.1.1-beta.1", None, "alpha", 0, None), "0.1.1-beta.2"), - (("0.1.1-rc.0", None, "alpha", 0, None), "0.1.1-rc.1"), - (("0.1.1-rc.0", None, "beta", 0, None), "0.1.1-rc.1"), -] - -weird_cases = [ - (("1.1", "PATCH", None, 0, None), "1.1.1"), - (("1", "MINOR", None, 0, None), "1.1.0"), - (("1", "MAJOR", None, 0, None), "2.0.0"), - (("1-alpha.0", None, "alpha", 0, None), "1.0.0-alpha.1"), - (("1-alpha.0", None, "alpha", 1, None), "1.0.0-alpha.1"), - (("1", None, "beta", 0, None), "1.0.0-beta.0"), - (("1", None, "beta", 1, None), "1.0.0-beta.1"), - (("1-beta", None, "beta", 0, None), "1.0.0-beta.1"), - (("1.0.0-alpha.1", None, "alpha", 0, None), "1.0.0-alpha.2"), - (("1", None, "rc", 0, None), "1.0.0-rc.0"), - (("1.0.0-rc.1+e20d7b57f3eb", "PATCH", None, 0, None), "1.0.0"), -] - -# test driven development -tdd_cases = [ - (("0.1.1", "PATCH", None, 0, None), "0.1.2"), - (("0.1.1", "MINOR", None, 0, None), "0.2.0"), - (("2.1.1", "MAJOR", None, 0, None), "3.0.0"), - (("0.9.0", "PATCH", "alpha", 0, None), "0.9.1-alpha.0"), - (("0.9.0", "MINOR", "alpha", 0, None), "0.10.0-alpha.0"), - (("0.9.0", "MAJOR", "alpha", 0, None), "1.0.0-alpha.0"), - (("0.9.0", "MAJOR", "alpha", 1, None), "1.0.0-alpha.1"), - (("1.0.0-alpha.2", None, "beta", 0, None), "1.0.0-beta.0"), - (("1.0.0-alpha.2", None, "beta", 1, None), "1.0.0-beta.1"), - (("1.0.0-beta.1", None, "rc", 0, None), "1.0.0-rc.0"), - (("1.0.0-rc.1", None, "rc", 0, None), "1.0.0-rc.2"), - (("1.0.0-alpha.0", None, "rc", 0, None), "1.0.0-rc.0"), - (("1.0.0-alpha.1", None, "alpha", 0, None), "1.0.0-alpha.2"), -] +from tests.utils import VersionSchemeTestArgs @pytest.mark.parametrize( - "test_input, expected", - itertools.chain(tdd_cases, weird_cases, simple_flow, linear_prerelease_cases), + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.1.2", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="2.1.1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "3.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.9.1-alpha.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MINOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.10.0-alpha.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-alpha.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.9.0", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-alpha.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.2", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-beta.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.2", + increment=None, + prerelease="beta", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-beta.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-beta.1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-rc.1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc.2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-alpha.2", + ), + # weird_cases + ( + VersionSchemeTestArgs( + current_version="1.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="1-alpha.0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-alpha.1", + ), + ( + VersionSchemeTestArgs( + current_version="1-alpha.0", + increment=None, + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-alpha.1", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-beta.0", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="beta", + prerelease_offset=1, + devrelease=None, + ), + "1.0.0-beta.1", + ), + ( + VersionSchemeTestArgs( + current_version="1-beta", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-beta.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-alpha.2", + ), + ( + VersionSchemeTestArgs( + current_version="1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-rc.1+e20d7b57f3eb", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.0", + ), + # simple_flow + ( + VersionSchemeTestArgs( + current_version="0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=1, + ), + "0.1.1-dev.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.2.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.2.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=1, + ), + "0.3.0-dev.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.3.1-alpha.0", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1-alpha.0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.3.1-alpha.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.0", + increment="PATCH", + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "0.3.1-alpha.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1-alpha.0", + increment=None, + prerelease="alpha", + prerelease_offset=1, + devrelease=None, + ), + "0.3.1-alpha.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1-alpha.0", + increment=None, + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.3.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "0.3.2", + ), + ( + VersionSchemeTestArgs( + current_version="0.4.2", + increment="MAJOR", + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-alpha.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-alpha.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-alpha.2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0-alpha.2.dev.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.2.dev.0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0-alpha.3.dev.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.2.dev.0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=0, + ), + "1.0.0-alpha.3.dev.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.1", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-beta.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-beta.0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-beta.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-beta.1", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-rc.0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-rc.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-rc.0", + increment=None, + prerelease="rc", + prerelease_offset=0, + devrelease=1, + ), + "1.0.0-rc.1.dev.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-rc.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0-alpha.3.dev.0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "1.0.0-beta.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.1", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.0.2", + ), + ( + VersionSchemeTestArgs( + current_version="1.0.2", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.1.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.1.0", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="1.2.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "1.2.1", + ), + ( + VersionSchemeTestArgs( + current_version="1.2.1", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "2.0.0", + ), + # linear prerelease cases (never bump backwards on pre-releases) + ( + VersionSchemeTestArgs( + current_version="0.1.1-beta.1", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1-beta.2", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1-rc.0", + increment=None, + prerelease="alpha", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1-rc.1", + ), + ( + VersionSchemeTestArgs( + current_version="0.1.1-rc.0", + increment=None, + prerelease="beta", + prerelease_offset=0, + devrelease=None, + ), + "0.1.1-rc.1", + ), + ], ) -def test_bump_semver_version(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] +def test_bump_semver_version( + version_args: VersionSchemeTestArgs, expected_version: str +): assert ( str( - SemVer2(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, + SemVer2(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, ) ) - == expected + == expected_version ) -@pytest.mark.parametrize("test_input,expected", local_versions) -def test_bump_semver_version_local(test_input, expected): - current_version = test_input[0] - increment = test_input[1] - prerelease = test_input[2] - prerelease_offset = test_input[3] - devrelease = test_input[4] - is_local_version = True +@pytest.mark.parametrize( + "version_args, expected_version", + [ + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.1.0", + increment="PATCH", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+0.1.1", + ), + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.1.1", + increment="MINOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+0.2.0", + ), + ( + VersionSchemeTestArgs( + current_version="4.5.0+0.2.0", + increment="MAJOR", + prerelease=None, + prerelease_offset=0, + devrelease=None, + ), + "4.5.0+1.0.0", + ), + ], +) +def test_bump_semver_version_local( + version_args: VersionSchemeTestArgs, expected_version: str +): assert ( str( - SemVer2(current_version).bump( - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, - is_local_version=is_local_version, + SemVer2(version_args.current_version).bump( + increment=version_args.increment, + prerelease=version_args.prerelease, + prerelease_offset=version_args.prerelease_offset, + devrelease=version_args.devrelease, + is_local_version=True, ) ) - == expected + == expected_version ) @@ -136,76 +669,3 @@ def test_semver_scheme_property(): def test_semver_implement_version_protocol(): assert isinstance(SemVer2("0.0.1"), VersionProtocol) - - -def test_semver_sortable(): - test_input = [x[0][0] for x in simple_flow] - test_input.extend([x[1] for x in simple_flow]) - # randomize - random_input = [SemVer2(x) for x in random.sample(test_input, len(test_input))] - assert len(random_input) == len(test_input) - sorted_result = [str(x) for x in sorted(random_input)] - assert sorted_result == [ - "0.1.0", - "0.1.0", - "0.1.1-dev.1", - "0.1.1", - "0.1.1", - "0.2.0", - "0.2.0", - "0.2.0", - "0.3.0-dev.1", - "0.3.0", - "0.3.0", - "0.3.0", - "0.3.0", - "0.3.1-alpha.0", - "0.3.1-alpha.0", - "0.3.1-alpha.0", - "0.3.1-alpha.0", - "0.3.1-alpha.1", - "0.3.1-alpha.1", - "0.3.1-alpha.1", - "0.3.1", - "0.3.1", - "0.3.1", - "0.3.2", - "0.4.2", - "1.0.0-alpha.0", - "1.0.0-alpha.0", - "1.0.0-alpha.1", - "1.0.0-alpha.1", - "1.0.0-alpha.1", - "1.0.0-alpha.1", - "1.0.0-alpha.2.dev.0", - "1.0.0-alpha.2.dev.0", - "1.0.0-alpha.2.dev.1", - "1.0.0-alpha.2", - "1.0.0-alpha.3.dev.0", - "1.0.0-alpha.3.dev.0", - "1.0.0-alpha.3.dev.1", - "1.0.0-beta.0", - "1.0.0-beta.0", - "1.0.0-beta.0", - "1.0.0-beta.1", - "1.0.0-beta.1", - "1.0.0-rc.0", - "1.0.0-rc.0", - "1.0.0-rc.0", - "1.0.0-rc.0", - "1.0.0-rc.1.dev.1", - "1.0.0-rc.1", - "1.0.0", - "1.0.0", - "1.0.1", - "1.0.1", - "1.0.2", - "1.0.2", - "1.1.0", - "1.1.0", - "1.2.0", - "1.2.0", - "1.2.1", - "1.2.1", - "2.0.0", - ] diff --git a/tests/test_version_schemes.py b/tests/test_version_schemes.py index 8e2dae9027..afaae76188 100644 --- a/tests/test_version_schemes.py +++ b/tests/test_version_schemes.py @@ -7,13 +7,18 @@ else: import importlib_metadata as metadata +from typing import TYPE_CHECKING + import pytest -from pytest_mock import MockerFixture -from commitizen.config.base_config import BaseConfig from commitizen.exceptions import VersionSchemeUnknown from commitizen.version_schemes import Pep440, SemVer, get_version_scheme +if TYPE_CHECKING: + from pytest_mock import MockerFixture + + from commitizen.config.base_config import BaseConfig + def test_default_version_scheme_is_pep440(config: BaseConfig): scheme = get_version_scheme(config.settings) diff --git a/tests/utils.py b/tests/utils.py index 5e26b2d70a..bea7f20a1d 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -4,12 +4,16 @@ import time import uuid from pathlib import Path +from typing import TYPE_CHECKING, NamedTuple import pytest from deprecated import deprecated from commitizen import cmd, exceptions, git +if TYPE_CHECKING: + from commitizen.version_schemes import Increment, Prerelease + skip_below_py_3_10 = pytest.mark.skipif( sys.version_info < (3, 10), reason="The output message of argparse is different between Python 3.10 and lower than Python 3.10", @@ -21,6 +25,14 @@ ) +class VersionSchemeTestArgs(NamedTuple): + current_version: str + increment: Increment | None + prerelease: Prerelease | None + prerelease_offset: int + devrelease: int | None + + class FakeCommand: def __init__(self, out=None, err=None, return_code=0): self.out = out