Skip to content

Commit d298635

Browse files
committed
docs: add a Python API page
commit_check.api had no page on the site. The core README carried it until the README pass (commit-check/commit-check#587) cut it to one example, on the understanding that the site would pick it up. guides/python-api.md covers all six functions -- including validate_tag and validate_push, which the old README never listed -- what each reads from git when a value is left out, the result shape and what pass, fail, skip and warn mean, and that the API does not read cchk.toml: config is a dict merged over the defaults, so a repository's file has to be loaded and passed in, and inherit_from is not followed. Every Python sample was run against commit-check 2.18.1 and its output pasted as printed. The page is in the nav under Guides and is linked from Where to run it and from the end of Reading the JSON.
1 parent 425fa24 commit d298635

4 files changed

Lines changed: 176 additions & 0 deletions

File tree

‎docs/example.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,3 +317,6 @@ $ commit-check -m -b --format json | jq '{status, warnings}'
317317
"warnings": 1
318318
}
319319
```
320+
321+
From Python, [`commit_check.api`](guides/python-api.md) returns the same
322+
result as a `dict`, without a subprocess.

‎docs/guides/integrations.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ developer sees locally and what is enforced on the pull request.
1111
| [GitHub App](github-app.md) | On every push and pull request, hosted | No | Install once for the organization |
1212
| [Command line](../example.md) | Wherever you call it: a range of commits, a CI you write yourself | — | `pip install commit-check` |
1313
| [MCP server](mcp.md) | Before the commit exists, inside an AI coding agent | — | One entry in the agent's MCP config |
14+
| [Python API](python-api.md) | Wherever your own code runs: a bot, a server-side hook, an agent you build | — | `pip install commit-check`, then `import commit_check.api` |
1415
| [Organization config](organization.md) | Everywhere at once | — | One shared `cchk.toml`, whichever of the above runs it |
1516

1617
## Which one
@@ -68,6 +69,11 @@ push and pull request, with no workflow file and no CI minutes.
6869
The same rules as tools for an AI coding agent, so the message is right before
6970
the commit exists. One entry in the agent's MCP settings. [Set it up →](mcp.md)
7071

72+
## Python API
73+
74+
The same checks from your own code, with no subprocess: each call returns the
75+
JSON result as a `dict`. [Read the API →](python-api.md)
76+
7177
## Across an organization { #across-an-organization }
7278

7379
One `cchk.toml` in the organization's `.github` repository; each repository

‎docs/guides/python-api.md‎

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Python API
2+
3+
`commit_check.api` runs the same checks as the command line from Python code —
4+
a bot, a server-side hook, an agent you are building — without starting a
5+
subprocess. Each function takes the value to check and returns a plain `dict`
6+
in the shape `commit-check --format json` prints, ready to branch on, log or
7+
hand to a model. It ships with the package: `pip install commit-check` is all
8+
it needs.
9+
10+
```python
11+
from commit_check.api import validate_message
12+
13+
result = validate_message("Fix: add streaming support")
14+
for check in result["checks"]:
15+
if check["status"] == "fail":
16+
print(check["rule_id"], check["suggest"])
17+
print("fix:", check["fix"])
18+
```
19+
20+
```text
21+
CC001 Use "fix: add streaming support"
22+
fix: fix: add streaming support
23+
```
24+
25+
## Functions
26+
27+
| Function | Checks | When the value is left out |
28+
|---|---|---|
29+
| `validate_message(message, *, config=None)` | The commit message: Conventional Commits, subject length, case and mood, body, sign-off, AI attribution — whichever rules the config enables | Required |
30+
| `validate_branch(branch=None, *, config=None)` | The branch name, and its rebase target when one is configured | The current branch, from `git branch --show-current` |
31+
| `validate_author(name=None, email=None, *, config=None)` | The author's name and email | Both come from `git config` |
32+
| `validate_tag(tag=None, *, config=None)` | Tag names, one per line | The tags pointing at `HEAD`; with none, the result is `skip` |
33+
| `validate_push(push_refs=None, *, config=None)` | That a push is not a force push; the check is always on here | Pass the pre-push lines, `<local ref> <local sha> <remote ref> <remote sha>`, one per line — without them there is nothing to compare |
34+
| `validate_all(message=None, branch=None, author_name=None, author_email=None, *, config=None)` | Message, branch and author together, in one result | Whatever is left out is not checked, and nothing is read from git |
35+
36+
The git lookups run in the current working directory, as the CLI's do.
37+
38+
```python
39+
from commit_check.api import validate_all
40+
41+
result = validate_all(message="feat: implement new feature", branch="user-login")
42+
print(result["status"])
43+
for check in result["checks"]:
44+
print(check["rule_id"], check["check"], check["status"])
45+
```
46+
47+
```text
48+
fail
49+
CC001 message pass
50+
CC004 subject_max_length pass
51+
CC005 subject_min_length pass
52+
CC201 branch fail
53+
```
54+
55+
## The result
56+
57+
Every function returns the same shape:
58+
59+
```python
60+
{
61+
"status": "pass" | "fail" | "skip",
62+
"warnings": 0, # how many checks have status "warn"
63+
"checks": [
64+
{
65+
"rule_id": "CC001",
66+
"check": "message",
67+
"status": "pass" | "fail" | "warn" | "skip",
68+
"value": "...", # what was checked
69+
"error": "...", # why it failed
70+
"suggest": "...", # advice for a person
71+
"fix": "...", # the corrected value, or "" when it takes judgment
72+
"docs_url": "https://commit-check.com/rules/#cc001",
73+
},
74+
],
75+
}
76+
```
77+
78+
- **Only `fail` is a rejection.** Code that branches on `status == "fail"`
79+
keeps working whatever else the result holds.
80+
- **`skip` is not `pass`.** A check skips when it never ran — the author is in
81+
`ignore_authors`, or there was nothing to check — and the top-level `status`
82+
is `skip` only when every check skipped. A skipped run validated nothing, so
83+
do not read it as approval.
84+
- **`warn`** is a rule listed under the config's `warn`: reported in full,
85+
never a failure. The top-level `status` stays `pass`, and `warnings` counts
86+
them.
87+
- **`fix`** is non-empty only when the correction is mechanical, so it can be
88+
applied as it stands; otherwise follow `suggest`.
89+
[Reading the JSON](../example.md#reading-the-json) lists the cases.
90+
91+
## Configuration
92+
93+
The API does not read `cchk.toml`. It starts from the built-in defaults and
94+
merges the `config` you pass, a `dict` shaped like the TOML file:
95+
96+
```python
97+
from commit_check.api import validate_message
98+
99+
result = validate_message(
100+
"docs: update readme",
101+
config={"commit": {"allow_commit_types": ["feat", "fix"]}},
102+
)
103+
print(result["status"])
104+
print(result["checks"][0]["suggest"])
105+
```
106+
107+
```text
108+
fail
109+
Use <type>(<scope>): <description>, where <type> is one of: feat, fix
110+
```
111+
112+
A top-level `warn` works as it does in the file:
113+
114+
```python
115+
from commit_check.api import validate_message
116+
117+
result = validate_message("add streaming support", config={"warn": ["message"]})
118+
print(result["status"], result["warnings"])
119+
```
120+
121+
```text
122+
pass 1
123+
```
124+
125+
To apply a repository's own policy, load its file and pass it along:
126+
127+
```python
128+
import tomllib # on Python 3.10: import tomli as tomllib
129+
130+
from commit_check.api import validate_message
131+
132+
with open("cchk.toml", "rb") as f:
133+
config = tomllib.load(f)
134+
135+
result = validate_message("feat: add streaming support", config=config)
136+
```
137+
138+
On Python 3.10, `tomli` is already installed: commit-check depends on it
139+
there. `inherit_from` is not followed this way; only the file's own keys
140+
apply. Every key is in the [configuration reference](../configuration.md).
141+
142+
## Fixing until it passes
143+
144+
An agent can apply `fix` and check again — the loop the
145+
[MCP server](mcp.md) teaches its clients:
146+
147+
```python
148+
from commit_check.api import validate_message
149+
150+
message = "Fix: add streaming support"
151+
for _ in range(3):
152+
result = validate_message(message)
153+
fixes = [c["fix"] for c in result["checks"] if c["status"] == "fail" and c["fix"]]
154+
if result["status"] != "fail" or not fixes:
155+
break
156+
message = fixes[0]
157+
158+
print(result["status"], message)
159+
```
160+
161+
```text
162+
pass fix: add streaming support
163+
```
164+
165+
When `fix` is empty the correction takes judgment — choosing a type for a bare
166+
subject, shortening a long one — and `suggest` says what is needed.

‎mkdocs.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ nav:
175175
- GitHub App: guides/github-app.md
176176
- Across an organization: guides/organization.md
177177
- MCP server: guides/mcp.md
178+
- Python API: guides/python-api.md
178179
- Policy guides: guides/policies.md
179180
- Command-line recipes: example.md
180181
- Troubleshooting: troubleshoot.md

0 commit comments

Comments
 (0)