Skip to content

Open source · MIT licensed

Catch bad commits before they merge.

Commit Check validates commit messages, branch names, authors and sign-offs against one cchk.toml — in your commit hook, in CI, on every pull request and inside your AI agent. When the fix is obvious, it hands you the line.

Add to GitHub Actions
pip install commit-check
❯ git commit -m "Fix: add streaming support"
check commit messageFailed
- hook id: check-message
- exit code: 1
Commit rejected by Commit-Check.
CC001 message check failed ==> Fix: add streaming support
The commit message should follow Conventional Commits. See https://www.conventionalcommits.org
Suggest: Use "fix: add streaming support"
Docs: https://commit-check.com/rules/#cc001
❯ git commit -m "fix: add streaming support"
check commit messagePassed
[feature/streaming 77663ff] fix: add streaming support
  • MIT licensed, no account needed
  • Python 3.10 – 3.14 on Linux, macOS and Windows
  • Signed build provenance on every release
  • CLI and hook work with any Git host

commit-check-action

Every pull request, checked and explained.

The Action reports where your reviewers already look — a PR comment, the job summary, inline annotations — and exposes a JSON result the next step can gate on.

One report on the pull request, edited in place on every push.Turn it on with pr-comments: true

feat: add login page #128Open

alex wants to merge 3 commits into main from feature/add-login

github-actionsbot commentededited

Commit Check

2 of 5 checks failed

ScopeChecked valueFailed checks
Commit 2/3 (5584f46)bad msgCC001 message
Commit 3/3 (37d6def)Fix: handle empty passwordCC001 message

▸ Show all 5 checks

All 5 checks passed

▸ Show all 5 checks

Rules reference

alex force-pushed feature/add-login, rewording 2 commits

The same comment was updated. No new comment, no noise in the thread.

The full breakdown lands in the workflow run: each check, the value, the fix.Turn it on with job-summary: true

Triggered via pull_requestalex pushed 5584f46

StatusFailure

Total duration14s

commit-check summary

Commit Check

2 of 5 checks failed

▾ Show all 5 checks

Commit message
✔ PR title (feat: add login page)
✔ Commit 1/3 (d87faca) (feat: add login page)
✖ Commit 2/3 (5584f46) (1 failure)
CC001 message
value: bad msg
Suggest: Use <type>(<scope>): <description>, where <type> is one of: feat, fix, docs, style, refactor, test, chore, perf, build, ci
✖ Commit 3/3 (37d6def) (1 failure)
CC001 message
value: Fix: handle empty password
Fix: fix: handle empty password
Branch
✔ Branch (feature/add-login)

Squash merges turn the PR title into the commit, so the title is checked too.Turn it on with pr-title: true

add login page #128Open

feat: add login pageSave

feat: add login page #128Open

alex wants to merge 3 commits into main from feature/add-login

Checks

Commit Check / commit-check (pull_request)CC001 message · PR title (add login page)Re-running on the edited title…✔ PR title (feat: add login page)

Squash and merge creates this commit on main:

feat: add login page (#128)

Squash and mergeSquash and mergeA required check has not passedAll checks have passed

Every commit in the pull request, checked against Conventional Commits.On by default: message: true

feat: add login page #128Open

alex wants to merge 3 commits into main from feature/add-login

Commit message

feat: add login paged87faca
bad msg5584f46
Fix: handle empty password37d6def

Annotations 2 errors

CC001 message

Commit 2/3 (5584f46): The commit message should follow Conventional Commits.
value: bad msg
Suggest: Use <type>(<scope>): <description>, where <type> is one of: feat, fix, docs, …

CC001 message

Commit 3/3 (37d6def): The commit message should follow Conventional Commits.
value: Fix: handle empty password
Fix: fix: handle empty password

Branch names checked against Conventional Branch, with the rename to run.On by default: branch: true

feat: add login page #131Open

alex wants to merge 1 commit into main from Feature/Add-Login

BranchFeature/Add-Login

CC201 The branch should follow Conventional Branch.

value: Feature/Add-Login
Fix: feature/Add-Login

❯ git branch -m feature/Add-Login
❯ commit-check --branch && echo passed
passed

Conventional Branch types

  • feature/
  • bugfix/
  • hotfix/
  • release/
  • chore/

main and master always pass. Add your own types in cchk.toml.

.github/workflows/commit-check.yml

on: pull_request: types: [opened, synchronize, reopened, edited] permissions: contents: read pull-requests: write jobs: commit-check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: fetch-depth: 0 - uses: commit-check/commit-check-action@v2 with: pr-title: true pr-comments: true

Make it a required check

Branch protection does the rest: a violating commit cannot merge, however it was made.

Verified before it installs

The Action checks the wheel's signed build provenance and fails the step if it does not match.

Try it without blocking anyone

dry-run: true reports every finding as a warning and always exits 0.

What it checks

Rules grouped by what they protect.

A few are on by default. Turn on what your project needs, rule by rule, or set any rule to warn instead of fail.

Browse all rules →
  • Commit messagesCC001–CC012

    Conventional Commits, subject length, imperative mood, sign-off, and merge, revert, fixup or WIP commits.

    Rejected: Fix: add streaming support

    Accepted: fix: add streaming support

  • Branch namesCC201–CC202

    Conventional Branch naming, and whether the branch is rebased onto its target.

    Rejected: my-new-thing

    Accepted: feature/streaming-support

  • AI attributionCC013–CC016

    Spots the trailers Claude Code, Cursor and others add. Forbid them, or require a disclosure instead.

    Rejected: Co-authored-by: Claude

    Accepted: Assisted-by: Claude

  • Author identityCC101–CC102

    Names and emails that match the patterns you set — here author_email_pattern = "@acme\.dev$" — so a build box cannot write itself into the history.

    Rejected: ec2-user <root@ip-10-0-0-12>

    Accepted: Jane Doe <jane@acme.dev>

  • Pushes and filesCC301–CC304

    No force-pushes, no oversized files, no forbidden paths — caught at pre-push, before they leave the laptop.

    Rejected: git push --force origin main

    Rejected: assets/model.bin, 240 MB

  • Across an organization

    One shared policy for every repository. Each repo inherits it and overrides only what differs.

    inherit_from = "github:acme/.github:cchk.toml"

    Organization guide →

Every finding

Not just “failed”. What failed, and the fix.

  1. A stable rule ID that links to its documentation.
  2. The exact value that was checked.
  3. Why it failed, in one sentence.
  4. A fix you can paste, whenever the correction is unambiguous.

$ echo "Fix: add streaming support" | commit-check -m

1CC001 message check failed ==>

2Fix: add streaming support

3The commit message should follow Conventional Commits.

4Suggest: Use "fix: add streaming support"

Docs: https://commit-check.com/rules/#cc001

  • 1.6M+downloads on PyPI
  • 160+repositories run the Action
  • Apacheallowlists the Action for its projects' CI

In the CI ofApacheTexas InstrumentsMilaIstio EcosystemOpenDriveLaband more →

Pricing

Free for open source. Team plan, soon.

The CLI, the hook, the Action and the MCP server are MIT licensed — nothing to buy. Plans only cover the hosted GitHub App, the one surface we run for you.

Nothing is blocked while you try it: without a config file the App reports its findings but leaves the check run neutral. GitHub's own commit-metadata rules sit behind its Enterprise plan — the arithmetic, and the caveats →

Questions

Does it read my source code?

No. The CLI validates commit metadata and never opens your files. The hosted App uses a blob-filtered fetch and a sparse checkout that materializes only the config files, so no other repository content is ever downloaded. Content scanning is deliberately out of scope.

Can a developer bypass it?

The pre-commit hook, yes: git commit --no-verify is one flag, and a local hook is there for fast feedback. The enforcement boundary is CI. Make the Action or the App a required status check and a violating change cannot merge, however it was committed.

Will turning it on block everyone tomorrow?

No. Without a config file the App reports in full but leaves the check neutral, and it never rejects a push. Most rules are off until you turn them on — the rules reference marks which start on.

What about the history I already have?

Only new commits are checked. Nothing asks you to rewrite what is already merged.

Does it only work on GitHub?

The CLI and the pre-commit hook run anywhere Git does — GitLab, Gitea, Bitbucket, a local machine. The Action and the App are GitHub-specific because they integrate with GitHub's check runs.

Do I need Node.js?

No. On a modern Python there are no runtime dependencies at all.

Which Python versions are supported?

3.10 through 3.14. CI runs the suite on all five, across Linux, macOS and Windows — fifteen combinations on every change.

How do I know the package I installed is the one you built?

Every release carries a signed build provenance attestation naming the workflow in this repository that built it. Check a wheel yourself with gh attestation verify <file> --repo commit-check/commit-check; the GitHub Action runs the same check before it installs anything, and fails the step if verification does not pass.

Who is behind this?

Commit Check is written and maintained by Xianpeng Shen, who also runs the hosted App. The engine, the Action, the App and the MCP server are open source under the commit-check organization — if the hosted App ever stops, the GitHub Action reads the same config and reports the same rule IDs.

Start with two commands.

No config file needed. The defaults check Conventional Commits, Conventional Branch and subject length; tighten them when you are ready.

$ pip install commit-check

$ commit-check --message --branch