chore(hooks): enforce Conventional Commits and Conventional Branches#28703
chore(hooks): enforce Conventional Commits and Conventional Branches#28703yassin-berriai wants to merge 2 commits into
Conversation
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 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 Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Greptile SummaryThis 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.
Confidence Score: 5/5Safe 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.
|
| 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
| 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 "..." |
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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).
| subjectPattern: ^(?![A-Z]).+$ | ||
| subjectPatternError: | | ||
| The subject "{subject}" must start with a lowercase character. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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>
Goal
Add opt-in local git hooks (and a complementary CI check) that enforce two community specs:
<type>(<scope>)!: <description>where<type>is one offeat | fix | docs | style | refactor | perf | test | build | ci | chore | revert.<type>/<description>where<type>is one offeature | 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
.githooks/commit-msgfixup!/squash!/amend!messages pass through;--no-verifystill works..githooks/pre-pushscripts/install_git_hooks.shcore.hooksPath=.githooksand normalizes the exec bits.Makefile(install-hooks)make install-hooksruns the installer. Opt-in, not chained intoinstall-dev..github/workflows/conventional-commits.ymlamannn/action-semantic-pull-requestpinned 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.pyCONTRIBUTING.md--no-verifyescape hatch.Branch policy
Allowed:
feature/<desc>,bugfix/<desc>,hotfix/<desc>,release/<desc>,chore/<desc>.Always bypassed (so internal workflow keeps working):
mainlitellm_internal_stagingdependabot/*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.pyports those cases to pytest and adds a few more (mixed-batch pre-push, comment-only commit message, multi-lineCOMMIT_EDITMSGlike the one git produces). The test is skipped on systems withoutbash.The PR itself dogfoods both hooks — the branch name (
chore/...) was validated bypre-push, and the commit subject (chore(hooks): ...) bycommit-msg.Out of scope
make install-hooks(CI PR-title check is the gate that catches everyone else).prepare-commit-msgtemplate (can follow up if reviewers want it).Admin follow-up after merge
Add the new
Validate PR titlecheck to the required-checks list in branch protection formainandlitellm_internal_staging. Until then, the workflow runs on every PR but isn't blocking.🤖 Generated with Claude Code
Docs: BerriAI/litellm-docs#208