Skip to content

Commit 197ca17

Browse files
docs(contributing): add architecture overview
A general-audience map of Commitizen's subsystems (cli, commands, config, providers, changelog_formats, version_schemes, plugins) and how they wire together. Pairs with the existing contributor guide and is also the anchor for the new agent-facing docs that follow. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent effd5dd commit 197ca17

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

docs/contributing/architecture.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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

Comments
 (0)