|
| 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. |
0 commit comments