Skip to content

chore(hooks): enforce Conventional Commits and Conventional Branches#28703

Open
yassin-berriai wants to merge 2 commits into
litellm_internal_stagingfrom
chore/conventional-commits-and-branches-hooks
Open

chore(hooks): enforce Conventional Commits and Conventional Branches#28703
yassin-berriai wants to merge 2 commits into
litellm_internal_stagingfrom
chore/conventional-commits-and-branches-hooks

Conversation

@yassin-berriai
Copy link
Copy Markdown
Contributor

@yassin-berriai yassin-berriai commented May 23, 2026

Goal

Add opt-in local git hooks (and a complementary CI check) that enforce two community specs:

  • Conventional Commits 1.0.0 — commit subjects must be <type>(<scope>)!: <description> where <type> is one of feat | fix | docs | style | refactor | perf | test | build | ci | chore | revert.
  • Conventional Branches — branch names must be <type>/<description> where <type> is one of feature | bugfix | hotfix | release | chore.

Recent commits in this repo already follow Conventional Commits (feat(guardrails): ..., fix(bedrock): ..., chore(test): ...); the hook codifies existing practice. Branches do not currently follow the spec, so this introduces the convention going forward with a bypass list so protected branches keep working.

Resolves LIT-3306

What's in the PR

File Purpose
.githooks/commit-msg Bash hook — validates subject line against Conventional Commits. Merge / revert / fixup! / squash! / amend! messages pass through; --no-verify still works.
.githooks/pre-push Bash hook — validates branch name against Conventional Branches. Skips tag pushes and branch deletions.
scripts/install_git_hooks.sh One-shot installer — sets core.hooksPath=.githooks and normalizes the exec bits.
Makefile (install-hooks) make install-hooks runs the installer. Opt-in, not chained into install-dev.
.github/workflows/conventional-commits.yml PR-title check via amannn/action-semantic-pull-request pinned to v6.1.1's SHA. This is the actual gate, since squash-merge replaces the merge subject with the PR title.
tests/test_litellm/test_git_hooks.py Runs each hook as a subprocess against representative valid / invalid / bypass / git-generated-message cases.
CONTRIBUTING.md Documents the conventions, the install step, the protected-branch list, and the --no-verify escape hatch.

Branch policy

Allowed: feature/<desc>, bugfix/<desc>, hotfix/<desc>, release/<desc>, chore/<desc>.

Always bypassed (so internal workflow keeps working):

  • main
  • litellm_internal_staging
  • dependabot/*
  • gh-readonly-queue/*

Existing branches (litellm_fix/..., ui/..., etc.) are not renamed by this PR — the policy applies to new branches going forward.

Testing

Hook scripts smoke-tested locally with 18 cases (9 commit-msg, 9 pre-push) covering valid types, invalid types, missing colons, breaking-change !, merge / revert / fixup! passthroughs, the empty-message edge case, the protected-branch bypass list, tag pushes, and branch deletions. All cases match expected exit codes.

tests/test_litellm/test_git_hooks.py ports those cases to pytest and adds a few more (mixed-batch pre-push, comment-only commit message, multi-line COMMIT_EDITMSG like the one git produces). The test is skipped on systems without bash.

The PR itself dogfoods both hooks — the branch name (chore/...) was validated by pre-push, and the commit subject (chore(hooks): ...) by commit-msg.

Out of scope

  • Renaming any existing branches.
  • Forcing hook installation on contributors who haven't run make install-hooks (CI PR-title check is the gate that catches everyone else).
  • A prepare-commit-msg template (can follow up if reviewers want it).

Admin follow-up after merge

Add the new Validate PR title check to the required-checks list in branch protection for main and litellm_internal_staging. Until then, the workflow runs on every PR but isn't blocking.

🤖 Generated with Claude Code

Docs: BerriAI/litellm-docs#208

Adds opt-in local git hooks plus a CI PR-title check:

- .githooks/commit-msg validates commit subjects against Conventional
  Commits 1.0.0 (feat|fix|docs|style|refactor|perf|test|build|ci|
  chore|revert)(scope)!: subject. Merge/revert/fixup!/squash!/amend!
  messages pass through; --no-verify still works.
- .githooks/pre-push validates branch names against Conventional
  Branches (feature|bugfix|hotfix|release|chore)/desc. Bypasses
  main, litellm_internal_staging, dependabot/*, gh-readonly-queue/*.
  Tag pushes and deletions are skipped.
- scripts/install_git_hooks.sh sets core.hooksPath=.githooks and is
  wired up as 'make install-hooks'. Opt-in — not chained into
  install-dev.
- .github/workflows/conventional-commits.yml validates PR titles via
  amannn/action-semantic-pull-request pinned to v6.1.1's SHA. This is
  the actual gate since squash-merge uses the PR title as the commit
  subject.
- tests/test_litellm/test_git_hooks.py exercises both hooks via
  subprocess for accept / reject / bypass / git-generated-message
  cases.
- CONTRIBUTING.md documents the conventions, the install step, the
  bypass list, and the --no-verify escape hatch.

Resolves LIT-3306

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@yassin-berriai yassin-berriai requested a review from a team May 23, 2026 16:57
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


Yassin Kortam seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 23, 2026

Greptile Summary

This PR introduces opt-in local git hooks and a CI workflow to enforce Conventional Commits on commit messages and Conventional Branches on branch names, codifying conventions that most recent commits in the repo already follow.

  • .githooks/commit-msg validates subject format with an ERE that correctly handles optional scope, breaking-change !, the lowercase-first-char guard (kept in sync with the CI subjectPattern), and pass-through for git-generated messages (merge, revert, fixup!, squash!, amend!).
  • .githooks/pre-push validates branch names, skipping deletions, tag refs, SHA-256 zero OIDs, and a well-defined protected-branch list; the CI workflow (amannn/action-semantic-pull-request, pinned by SHA) is the actual enforcement gate since squash-merge replaces the commit subject with the PR title.
  • tests/test_litellm/test_git_hooks.py ports all hook cases to pytest subprocess tests with no network calls, consistent with the repo's mock-only test policy for this folder.

Confidence Score: 5/5

Safe to merge; changes are purely additive with no modifications to existing production code paths.

All changed files are new additions — bash scripts, a GitHub Actions workflow, an installer, and tests. No existing logic is modified. The hook scripts are opt-in and cannot affect contributors who haven't run make install-hooks; the CI workflow is the real gate. Regex edge cases are all covered by the test suite.

No files require special attention.

Important Files Changed

Filename Overview
.githooks/commit-msg New bash hook enforcing Conventional Commits; regex is well-formed, pass-through cases for git-generated messages are correct, and the lowercase-first-char guard is deliberately kept in sync with the CI workflow's subjectPattern.
.githooks/pre-push New bash hook enforcing Conventional Branches; correctly handles deletions (zero OID), tag refs, SHA-256 OIDs, protected-branch bypasses, and multi-ref batches in a single push.
.github/workflows/conventional-commits.yml New CI workflow pinned to a commit SHA; permissions correctly scoped to pull-requests:read; type list mirrors the local hook; ignoreLabels escape hatch included.
scripts/install_git_hooks.sh Idempotent installer that sets core.hooksPath and normalizes exec bits; uses set -euo pipefail and validates it's inside a git worktree before making any changes.
tests/test_litellm/test_git_hooks.py Comprehensive pytest suite covering 18+ hook scenarios via subprocess; no network calls; skips gracefully on non-bash systems; autouse fixture normalizes exec bit on fresh clones.
Makefile Adds install-hooks to .PHONY and help text; correctly kept opt-in (not chained into install-dev).
CONTRIBUTING.md Updates quick-start examples to use conventional branch/commit format and adds a two-sentence pointer section to the external docs site; no substantial documentation added inline.

Reviews (2): Last reviewed commit: "fix(hooks): address Greptile review on P..." | Re-trigger Greptile

Comment thread CONTRIBUTING.md Outdated
Comment on lines +51 to +112
make help
```

That's it! Your local development environment is ready.

## Commit and Branch Conventions

LiteLLM enforces two community specs:

- **Commits** follow [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/) — `<type>(<scope>)!: <description>`
- **Branches** follow [Conventional Branches](https://conventional-branch.github.io/) — `<type>/<description>`

### Commit message format

```
<type>(<optional scope>)!: <description>

<optional body>

<optional footer>
```

Allowed `<type>` values: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`. Add `!` before `:` for breaking changes.

Examples:
```
feat(router): add weighted round-robin strategy
fix(bedrock): decouple STS region from aws_region_name
chore(deps): bump black to 26.3.1
refactor!: drop Python 3.8 support
```

PR titles must follow the same format — squash-merge uses the PR title as the commit subject, and a CI check validates it.

### Branch naming

Format: `<type>/<short-description>` where `<type>` is one of `feature`, `bugfix`, `hotfix`, `release`, `chore`.

Examples:
```
feature/weighted-round-robin
bugfix/streaming-empty-chunks
chore/bump-black
hotfix/auth-bypass
```

Branches always allowed (bypass the check): `main`, `litellm_internal_staging`, `dependabot/*`, `gh-readonly-queue/*`.

### Installing the hooks

The hooks live in `.githooks/` and are opt-in. Run once per clone:

```bash
make install-hooks
```

This sets `core.hooksPath=.githooks` for the local repository. The hooks run on `git commit` (subject validation) and `git push` (branch validation).

In a rare emergency you can bypass them per-command:

```bash
git commit --no-verify -m "..."
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Documentation belongs in litellm-docs repo

The team convention is that documentation must live in the litellm-docs repository, not in this repo. The new "Commit and Branch Conventions" section (covering commit format, branch naming, hook installation, and bypass instructions) is substantial documentation that should be added there instead of to CONTRIBUTING.md here.

Rule Used: Prevent documentation from being added - needs to ... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8f0b29b. CONTRIBUTING.md is now a 2-line pointer to docs.litellm.ai, and the full Commit-and-Branch-Conventions section has been added to the litellm-docs repo in BerriAI/litellm-docs#208 (docs/extras/contributing_code.md).

Comment on lines +41 to +43
subjectPattern: ^(?![A-Z]).+$
subjectPatternError: |
The subject "{subject}" must start with a lowercase character.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 subjectPattern adds a restriction absent from the local hook

The CI enforces ^(?![A-Z]).+$ (description must not start with an uppercase letter), but the local commit-msg hook pattern ": .+" imposes no such constraint. A developer can commit feat: Add feature locally (hook passes), open a PR with that as the title, and the CI job will fail with a confusing error about uppercase. Adding the same lowercase guard to PATTERN in .githooks/commit-msg would keep the two enforcement points in sync.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8f0b29b. The local commit-msg regex now also rejects an uppercase first letter in the description (: [^A-Z].*), with a comment in the script pointing at this workflow as the reason for the rule. The hook is now the strictly tighter of the two gates, so anything that passes locally will pass here. Tests were extended to cover the new rejection plus the digit/symbol-start cases that remain allowed.

Resolves two findings from the automated code review:

1. CONTRIBUTING.md: shrink the new Conventional Commits / Branches
   section to a 2-line pointer at docs.litellm.ai. Per the team
   convention, the full documentation lives in the litellm-docs
   repo — see BerriAI/litellm-docs#208 for the companion change that
   adds the section to docs/extras/contributing_code.md.

2. .githooks/commit-msg: tighten the subject regex to also reject an
   uppercase first letter in the description. CI's subjectPattern is
   ^(?![A-Z]).+$ so the previous local hook would accept 'feat: Add
   thing' which would then fail the PR-title check. The local hook is
   now the strictly tighter of the two gates. Test cases extended to
   cover both the new rejection and the digit/symbol-start cases that
   remain allowed.

Resolves LIT-3306

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@yassin-berriai
Copy link
Copy Markdown
Contributor Author

@greptileai

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants