|
| 1 | +# Architecture Overview |
| 2 | + |
| 3 | +This page is a map of Commitizen's subsystems and extension points. It is aimed |
| 4 | +at contributors (human or AI) who are about to change code and need to know |
| 5 | +where things live and how they fit together. |
| 6 | + |
| 7 | +For end-user concepts, see the [Commands](../commands/init.md) and |
| 8 | +[Configuration](../config/configuration_file.md) sections. |
| 9 | + |
| 10 | +## Top-level layout |
| 11 | + |
| 12 | +``` |
| 13 | +commitizen/ |
| 14 | +├── cli.py # CLI entry point and argument parsing (uses decli) |
| 15 | +├── commands/ # One module per CLI subcommand (commit, bump, changelog, ...) |
| 16 | +├── config/ # Configuration discovery, parsing, and base classes |
| 17 | +├── providers/ # Version providers (where to read/write the version) |
| 18 | +├── changelog_formats/ # Changelog file format handlers (markdown, asciidoc, ...) |
| 19 | +├── cz/ # Built-in commit conventions (conventional_commits, jira, customize) |
| 20 | +├── version_schemes.py # Version schemes (pep440, semver, semver2) |
| 21 | +├── tags.py # Tag format parsing and matching |
| 22 | +├── changelog.py # Changelog generation engine |
| 23 | +├── bump.py # Version-bump engine |
| 24 | +├── defaults.py # Default settings and the Settings TypedDict |
| 25 | +├── exceptions.py # CLI-facing exception types and exit codes |
| 26 | +├── out.py # Standard output helpers |
| 27 | +├── git.py # Git wrapper used by all commands |
| 28 | +├── hooks.py # Pre/post bump hook execution |
| 29 | +└── templates/ # Built-in Jinja2 templates for changelog rendering |
| 30 | +``` |
| 31 | + |
| 32 | +## Extension points |
| 33 | + |
| 34 | +Commitizen is plugin-friendly. Four kinds of extensions can be registered by |
| 35 | +external packages via Python entry points; the built-in implementations use |
| 36 | +the same mechanism. |
| 37 | + |
| 38 | +| Kind | Entry-point group | Built-ins registered in `pyproject.toml` | Base class / Protocol | |
| 39 | +|---|---|---|---| |
| 40 | +| Commit convention | `commitizen.plugin` | `cz_conventional_commits`, `cz_jira`, `cz_customize` | `commitizen/cz/base.py:BaseCommitizen` | |
| 41 | +| Version provider | `commitizen.provider` | `cargo`, `commitizen`, `composer`, `npm`, `pep621`, `poetry`, `scm`, `uv` | `commitizen/providers/base_provider.py:VersionProvider` | |
| 42 | +| Version scheme | `commitizen.scheme` | `pep440`, `semver`, `semver2` | `commitizen/version_schemes.py:VersionProtocol` | |
| 43 | +| Changelog format | `commitizen.changelog_format` | `markdown`, `asciidoc`, `textile`, `restructuredtext` | `commitizen/changelog_formats/base.py:BaseFormat` | |
| 44 | + |
| 45 | +Each kind is loaded lazily via `importlib.metadata.entry_points(...)`. To add |
| 46 | +a new built-in implementation you register it in `pyproject.toml` under the |
| 47 | +appropriate `[project.entry-points."..."]` table. |
| 48 | + |
| 49 | +End-user documentation for these extension points lives elsewhere — see |
| 50 | +[Version Provider](../config/version_provider.md), |
| 51 | +[Customized Python Class](../customization/python_class.md), and |
| 52 | +[Changelog Template](../customization/changelog_template.md). This page |
| 53 | +focuses on where the source lives and how it is wired together. |
| 54 | + |
| 55 | +## Configuration layering |
| 56 | + |
| 57 | +Configuration is discovered, parsed, and exposed as a `Settings` TypedDict. |
| 58 | + |
| 59 | +1. **Discovery** — `commitizen/config/__init__.py:read_cfg` searches the |
| 60 | + working directory (and the git project root when different) for known |
| 61 | + config files in a defined order (see |
| 62 | + `commitizen/defaults.py:CONFIG_FILES`). |
| 63 | +2. **Format-specific parsing** — `commitizen/config/factory.py:create_config` |
| 64 | + dispatches to one of: |
| 65 | + - `commitizen/config/toml_config.py:TomlConfig` (TOML; includes |
| 66 | + `pyproject.toml` under `[tool.commitizen]`) |
| 67 | + - `commitizen/config/json_config.py:JsonConfig` |
| 68 | + - `commitizen/config/yaml_config.py:YAMLConfig` |
| 69 | +3. **Defaults merge** — every parser inherits from |
| 70 | + `commitizen/config/base_config.py:BaseConfig`, which starts from |
| 71 | + `commitizen/defaults.py:DEFAULT_SETTINGS` and overlays the user values. |
| 72 | +4. **Consumption** — commands read `config.settings[...]`; providers and |
| 73 | + formats receive the live `BaseConfig` so they can react to settings such |
| 74 | + as `encoding`, `tag_format`, and `version_scheme`. |
| 75 | + |
| 76 | +The `Settings` TypedDict in `defaults.py` is the authoritative list of |
| 77 | +recognized keys. Adding a new setting almost always means touching this file. |
| 78 | + |
| 79 | +## Command flow |
| 80 | + |
| 81 | +`cli.py` parses `argv` with [decli](https://github.com/woile/decli), resolves |
| 82 | +the chosen subcommand to a class under `commitizen/commands/`, then |
| 83 | +instantiates and calls it. A typical command: |
| 84 | + |
| 85 | +1. Reads `config.settings`. |
| 86 | +2. Resolves dependencies (provider, scheme, changelog format) via the |
| 87 | + `get_*` helpers in the respective subpackages. |
| 88 | +3. Does its work, surfacing user-visible text through `commitizen/out.py` |
| 89 | + and errors through `commitizen/exceptions.py` (each exception carries an |
| 90 | + exit code defined in `commitizen/exceptions.py` and documented in |
| 91 | + [Exit Codes](../exit_codes.md)). |
| 92 | + |
| 93 | +`cz commit` and `cz bump` are the most stateful commands — they call `git` |
| 94 | +through `commitizen/git.py`, run user-defined `pre_bump_hooks`/`post_bump_hooks` |
| 95 | +via `commitizen/hooks.py`, and may mutate version files through the active |
| 96 | +provider. |
| 97 | + |
| 98 | +## Templates and changelog rendering |
| 99 | + |
| 100 | +Changelog rendering uses Jinja2. Built-in templates live under |
| 101 | +`commitizen/templates/`. The template loader is a `ChoiceLoader` whose first |
| 102 | +loader is `FileSystemLoader(".")` and whose second loader is provided by the |
| 103 | +active commit-convention class (default: a `PackageLoader` for built-in |
| 104 | +templates), so a repository can override any built-in template by placing a |
| 105 | +file of the same name at the project root or in the configured template |
| 106 | +directory. |
| 107 | + |
| 108 | +## Tests mirror the source tree |
| 109 | + |
| 110 | +Tests are organized to mirror the source modules: |
| 111 | + |
| 112 | +| Source | Test | |
| 113 | +|---|---| |
| 114 | +| `commitizen/providers/*` | `tests/providers/`, `tests/test_factory.py` | |
| 115 | +| `commitizen/changelog_formats/*` | `tests/test_changelog_format_*.py`, `tests/test_changelog_formats.py` | |
| 116 | +| `commitizen/version_schemes.py` | `tests/test_version_schemes.py`, `tests/test_version_scheme_*.py` | |
| 117 | +| `commitizen/commands/*` | `tests/commands/`, `tests/test_cli/` | |
| 118 | +| `commitizen/config/*` | `tests/test_conf.py` | |
| 119 | +| `commitizen/bump.py` | `tests/test_bump_*.py` | |
| 120 | +| `commitizen/changelog.py` | `tests/test_changelog.py`, `tests/test_incremental_build.py` | |
| 121 | +| `commitizen/tags.py` | `tests/test_tags.py` | |
| 122 | + |
| 123 | +When you add or modify a subsystem, the targeted test file is usually |
| 124 | +obvious from this mirror. The |
| 125 | +[targeted-test map for agents](agents/validation.md#targeted-test-map) |
| 126 | +captures the most useful selectors. |
0 commit comments