ci: lint PR titles with commitlint (#37498)
## Summary - Enforce **Conventional Commits** on PR titles (PRs are squash-merged, so the PR title becomes the final commit message). - Add a local `make lint-pr-title` target so contributors can validate titles before pushing. ## Why We squash-merge PRs, which means the final repository history is largely shaped by **PR titles**. Enforcing a consistent Conventional Commits format makes: - **Release notes & changelogs easier to generate** (types like `feat` / `fix` can be grouped automatically). - **History easier to scan** (uniform structure, optional scopes, explicit breaking changes via `!`). - **Automation more reliable** (future tooling can infer category and scope from the title). ## PR title format ```text type(scope)!: subject type: one of build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test scope: optional (e.g. web, api, actions, repo, …) !: optional, indicates a breaking change subject: short, imperative, no trailing period ``` ## Examples ```text feat(web): add dark mode toggle fix(api): avoid panic when repo is missing chore(ci): lint PR titles with commitlint refactor(templates): reduce duplication in repo list rendering feat!: remove legacy OAuth endpoint ``` ## Local testing ```text make deps-frontend make lint-pr-title PR_TITLE="feat(web): add dark mode toggle" ``` --------- Signed-off-by: Nicolas <bircni@icloud.com> Co-authored-by: nb <nb@users.noreply.local> Co-authored-by: GPT-5.2 <gpt-5.2@openai.com>
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
<!-- start tips -->
|
<!-- start tips -->
|
||||||
Please check the following:
|
Please check the following:
|
||||||
1. Make sure you are targeting the `main` branch, pull requests on release branches are only allowed for backports.
|
1. Make sure you are targeting the `main` branch, pull requests on release branches are only allowed for backports.
|
||||||
2. Make sure you have read contributing guidelines: https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md .
|
2. Use a Conventional Commits PR title, for example `fix(repo): handle empty branch names`.
|
||||||
3. For documentations contribution, please go to https://gitea.com/gitea/docs
|
3. Make sure you have read contributing guidelines: https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md .
|
||||||
4. Describe what your pull request does and which issue you're targeting (if any).
|
4. For documentations contribution, please go to https://gitea.com/gitea/docs
|
||||||
5. It is recommended to enable "Allow edits by maintainers", so maintainers can help more easily.
|
5. Describe what your pull request does and which issue you're targeting (if any).
|
||||||
6. Your input here will be included in the commit message when this PR has been merged. If you don't want some content to be included, please separate them with a line like `---`.
|
6. It is recommended to enable "Allow edits by maintainers", so maintainers can help more easily.
|
||||||
7. Delete all these tips before posting.
|
7. Your input here will be included in the commit message when this PR has been merged. If you don't want some content to be included, please separate them with a line like `---`.
|
||||||
|
8. Delete all these tips before posting.
|
||||||
<!-- end tips -->
|
<!-- end tips -->
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
name: pr-title
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- edited
|
||||||
|
- reopened
|
||||||
|
- synchronize
|
||||||
|
- ready_for_review
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint-pr-title:
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 5
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- run: make lint-pr-title
|
||||||
|
env:
|
||||||
|
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
- Run single playwright e2e test files with `GITEA_TEST_E2E_FLAGS='<filepath>' make test-e2e`
|
- Run single playwright e2e test files with `GITEA_TEST_E2E_FLAGS='<filepath>' make test-e2e`
|
||||||
- Add the current year into the copyright header of new `.go` files
|
- Add the current year into the copyright header of new `.go` files
|
||||||
- Ensure no trailing whitespace in edited files
|
- Ensure no trailing whitespace in edited files
|
||||||
|
- Use Conventional Commits format for commit messages and PR titles (e.g. `type(scope): subject`)
|
||||||
- Never force-push, amend, or squash unless asked. Use new commits and normal push for pull request updates
|
- Never force-push, amend, or squash unless asked. Use new commits and normal push for pull request updates
|
||||||
- Preserve existing code comments, do not remove or rewrite comments that are still relevant
|
- Preserve existing code comments, do not remove or rewrite comments that are still relevant
|
||||||
- In TypeScript, use `!` (non-null assertion) instead of `?.`/`??` when a value is known to always exist
|
- In TypeScript, use `!` (non-null assertion) instead of `?.`/`??` when a value is known to always exist
|
||||||
|
|||||||
@@ -189,6 +189,22 @@ In the PR title, describe the problem you are fixing, not how you are fixing it.
|
|||||||
Use the first comment as a summary of your PR. \
|
Use the first comment as a summary of your PR. \
|
||||||
In the PR summary, you can describe exactly how you are fixing this problem.
|
In the PR summary, you can describe exactly how you are fixing this problem.
|
||||||
|
|
||||||
|
PR titles must follow the [Conventional Commits](https://www.conventionalcommits.org/) format, because PRs are squash-merged and the PR title becomes the resulting commit message:
|
||||||
|
|
||||||
|
```text
|
||||||
|
type(scope)!: subject
|
||||||
|
```
|
||||||
|
|
||||||
|
The allowed types are `build`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `revert`, `style`, and `test`. The generic `chore` type is intentionally not accepted; pick a more descriptive type instead.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```text
|
||||||
|
fix(web): prevent avatar upload crash on empty file
|
||||||
|
feat(api): add pagination to repo hooks list
|
||||||
|
ci(workflows): lint PR titles with commitlint
|
||||||
|
```
|
||||||
|
|
||||||
Keep this summary up-to-date as the PR evolves. \
|
Keep this summary up-to-date as the PR evolves. \
|
||||||
If your PR changes the UI, you must add **after** screenshots in the PR summary. \
|
If your PR changes the UI, you must add **after** screenshots in the PR summary. \
|
||||||
If you are not implementing a new feature, you should also post **before** screenshots for comparison.
|
If you are not implementing a new feature, you should also post **before** screenshots for comparison.
|
||||||
|
|||||||
@@ -321,6 +321,10 @@ lint-md: node_modules ## lint markdown files
|
|||||||
lint-md-fix: node_modules ## lint markdown files and fix issues
|
lint-md-fix: node_modules ## lint markdown files and fix issues
|
||||||
pnpm exec markdownlint --fix *.md
|
pnpm exec markdownlint --fix *.md
|
||||||
|
|
||||||
|
.PHONY: lint-pr-title
|
||||||
|
lint-pr-title: ## lint PR title against Conventional Commits (set PR_TITLE=...)
|
||||||
|
@node ./tools/lint-pr-title.js
|
||||||
|
|
||||||
.PHONY: lint-spell
|
.PHONY: lint-spell
|
||||||
lint-spell: ## lint spelling
|
lint-spell: ## lint spelling
|
||||||
@git ls-files $(SPELLCHECK_FILES) | xargs go run $(MISSPELL_PACKAGE) -dict assets/misspellings.csv -error
|
@git ls-files $(SPELLCHECK_FILES) | xargs go run $(MISSPELL_PACKAGE) -dict assets/misspellings.csv -error
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import {env, exit} from 'node:process';
|
||||||
|
|
||||||
|
const allowedTypes = 'build, ci, docs, feat, fix, perf, refactor, revert, style, test';
|
||||||
|
const title = env.PR_TITLE;
|
||||||
|
|
||||||
|
if (!title) {
|
||||||
|
console.error('Missing PR_TITLE');
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const validTitlePattern = new RegExp(`^(${allowedTypes.replaceAll(', ', '|')})(\\([\\w.-]+\\))?(!)?: .+$`);
|
||||||
|
|
||||||
|
if (!validTitlePattern.test(title)) {
|
||||||
|
console.error(`Invalid PR title: ${title}`);
|
||||||
|
console.error('Expected format: type(scope): subject');
|
||||||
|
console.error(`Allowed types: ${allowedTypes}`);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user