ci(workflows): enforce Conventional Commits format on PR titles

This commit is contained in:
Yuneng Jiang 2026-05-07 13:18:53 -07:00
parent db8198faba
commit 54bb1c2bcc
No known key found for this signature in database
6 changed files with 152 additions and 10 deletions

View file

@ -1,3 +1,21 @@
<!--
PR title must follow Conventional Commits 1.0.0:
<type>(<optional scope>): <description>
Allowed types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
Use `!` after the type/scope for breaking changes (e.g. feat(api)!: drop /v1/foo).
Examples:
feat(mcp): add oauth2 authorization flow
fix(ui-teams): refresh table on member change
refactor(ui-playground): extract message renderer
A GitHub Actions check enforces this format. See:
https://www.conventionalcommits.org/en/v1.0.0/
CONTRIBUTING.md → Commit and PR Conventions
-->
## Relevant issues
<!-- e.g. "Fixes #000" -->
@ -45,14 +63,18 @@ If you're seeing a delay in your PR being merged, ping the LiteLLM Team on [Slac
## Type
<!-- Select the type of Pull Request -->
<!-- Keep only the necessary ones -->
<!-- Should match the type used in the PR title (Conventional Commits). Keep only the relevant one(s). -->
🆕 New Feature
🐛 Bug Fix
🧹 Refactoring
📖 Documentation
🚄 Infrastructure
✅ Test
- `feat` — New feature
- `fix` — Bug fix
- `refactor` — Refactoring (no behavior change)
- `docs` — Documentation
- `perf` — Performance improvement
- `test` — Tests only
- `build` — Build system / dependencies
- `ci` — CI / GitHub Actions
- `chore` — Maintenance / release / tooling
- `revert` — Revert a previous commit
- `style` — Formatting / whitespace
## Changes

69
.github/workflows/validate-pr-title.yml vendored Normal file
View file

@ -0,0 +1,69 @@
name: Validate PR Title
# Enforces Conventional Commits 1.0.0 on PR titles.
# PRs to litellm_internal_staging are squash-merged, so the PR title becomes
# the commit subject on the release-feeding branch. Mechanical parseability
# matters for changelogs and cherry-picks.
#
# Skipped on PRs to main: those are rollups from staging that preserve the
# underlying conventional commits, so the rollup title itself doesn't need
# to match.
on:
pull_request:
types: [opened, edited, reopened, synchronize]
branches-ignore:
- main
workflow_dispatch:
inputs:
title:
description: 'PR title to validate (for runtime testing)'
required: true
type: string
permissions:
contents: read
jobs:
validate:
name: Conventional Commits PR title
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: Validate title
env:
PR_TITLE: ${{ github.event.pull_request.title || inputs.title }}
run: |
set -u
echo "Validating title: $PR_TITLE"
# Conventional Commits 1.0.0 — allowed types per the spec plus
# the common extras the project uses. `security` is intentionally
# omitted: OSS commits use reliability/maintenance language.
# See: https://www.conventionalcommits.org/en/v1.0.0/
PATTERN='^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-z0-9._-]+(/[a-z0-9._-]+)*\))?!?: .+'
if [[ "$PR_TITLE" =~ $PATTERN ]]; then
echo "PR title matches Conventional Commits format."
exit 0
fi
echo "::error::PR title does not match Conventional Commits 1.0.0."
cat <<'EOF'
PR title must follow Conventional Commits 1.0.0:
<type>(<optional scope>): <description>
Allowed types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
Use `!` after the type/scope for breaking changes (e.g. feat(api)!: drop /v1/foo).
Examples:
feat(mcp): add oauth2 authorization flow
fix(ui-teams): refresh table on member change
refactor(ui-playground): extract message renderer
chore(release): merge internal staging into main
See: https://www.conventionalcommits.org/en/v1.0.0/
EOF
exit 1

View file

@ -140,6 +140,16 @@ When opening issues or pull requests, follow these templates:
- Clearly describe the feature
- Explain motivation and use case with concrete examples
### Pull Request Title Format
**PR titles must follow [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/)**: `<type>(<optional scope>): <description>`.
- Allowed types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`.
- Append `!` for breaking changes (e.g. `feat(api)!: drop /v1/foo`).
- Examples: `feat(mcp): add oauth2 authorization flow`, `fix(ui-teams): refresh table on member change`, `chore(release): merge internal staging into main`.
- The `validate-pr-title` GitHub Actions check enforces this on every PR (rollup PRs to `main` are exempt).
- Local commits on the PR branch don't need to match — `litellm_internal_staging` is squash-merge only, so the squash flattens to the PR title at merge time.
### Pull Requests (`.github/pull_request_template.md`)
- Add at least 1 test in `tests/litellm/`
- Ensure `make test-unit` passes

View file

@ -48,6 +48,10 @@ When contributing to the project, use the appropriate templates:
**Pull Requests** (`.github/pull_request_template.md`):
- Add at least 1 test in `tests/litellm/`
- Ensure `make test-unit` passes
- **PR title must follow [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/)** — `<type>(<optional scope>): <description>`. Allowed types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`. Use `!` for breaking changes. The `validate-pr-title` GHA enforces this on every PR (except rollups to `main`).
- Why: `litellm_internal_staging` is squash-merge only, so the PR title becomes the staging commit subject — keep it parseable for changelogs and cherry-picks. `main` is merge-only and preserves the underlying commits.
- Examples: `feat(mcp): add oauth2 authorization flow`, `fix(ui-teams): refresh table on member change`, `chore(release): merge internal staging into main`.
- Local commits on the PR branch don't have to match — the squash flattens to the PR title at merge time. Only the PR title needs to match.
## Architecture Overview

View file

@ -8,6 +8,7 @@ Here are the core requirements for any PR submitted to LiteLLM:
- [ ] **Sign the Contributor License Agreement (CLA)** - [see details](#contributor-license-agreement-cla)
- [ ] **Keep scope isolated** - Your changes should address 1 specific problem at a time
- [ ] **Use a Conventional Commits PR title** - [see details](#commit-and-pr-conventions)
#### Proxy (Backend) PRs
@ -67,9 +68,9 @@ make lint
# Run unit tests to ensure nothing is broken
make test-unit
# Commit your changes
# Commit your changes (see "Commit and PR Conventions" below for the title format)
git add .
git commit -m "Your descriptive commit message"
git commit -m "feat(scope): short description of your change"
# Push and create a PR
git push origin your-feature-branch
@ -312,6 +313,40 @@ Ensure the UI builds successfully before submitting your PR:
npm run build
```
## Commit and PR Conventions
LiteLLM follows [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/) for **PR titles**. A GitHub Actions check (`.github/workflows/validate-pr-title.yml`) enforces the format on every PR.
### Format
```
<type>(<optional scope>): <description>
```
- **Allowed types**: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`.
- **Scope** (optional): a short noun for the area touched, lowercase, e.g. `mcp`, `ui-teams`, `proxy-auth`.
- **Description**: short imperative-mood summary, no trailing period.
- **Breaking changes**: append `!` after the type/scope, e.g. `feat(api)!: drop /v1/foo`.
### Examples
- `feat(mcp): add oauth2 authorization flow`
- `fix(ui-teams): refresh table on member change`
- `refactor(ui-playground): extract message renderer`
- `docs: clarify proxy startup steps`
- `chore(release): merge internal staging into main`
### Why
Branches we care about have different merge strategies:
- **`litellm_internal_staging`** is **squash-merge only**, so the PR title becomes the squash commit subject. Conventional titles are mechanically parseable for changelogs and easy to cherry-pick.
- **`main`** is **merge-commit only** (no squash), so the underlying conventional commits are preserved through release rollups.
You don't need to rewrite local commit history on the PR branch; the squash flattens to the PR title at merge time. Only the **PR title** needs to match the convention.
> PRs to `main` (release rollups) are exempt from this check, since they aggregate already-conventional commits from staging.
## Submitting Your PR
1. **Push your branch**: `git push origin your-feature-branch`

View file

@ -482,6 +482,8 @@ make test-unit # Run unit tests
make format-check # Check formatting only
```
**PR titles follow [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/)** (e.g. `feat(mcp): add oauth2 flow`, `fix(ui-teams): refresh table`). A GitHub Actions check enforces the format. See [CONTRIBUTING.md → Commit and PR Conventions](CONTRIBUTING.md#commit-and-pr-conventions) for details.
For detailed contributing guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md).
> **📖 Contributing to documentation?** The LiteLLM docs have moved to a separate repository: [BerriAI/litellm-docs](https://github.com/BerriAI/litellm-docs). Please open doc PRs there. Docs are served at [docs.litellm.ai](https://docs.litellm.ai).