-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
ci(pr-lint): Enforce Sentry commit convention and PR description quality #16375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../.cursor/rules/contribution-guidelines.mdc |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../.cursor/rules/pr-description.mdc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| --- | ||
| description: PR description format and quality standards for sentry-docs | ||
| globs: | ||
| alwaysApply: false | ||
| --- | ||
|
|
||
| # PR Description & Quality Standards | ||
|
|
||
| ## Self-Review Checklist | ||
|
|
||
| Before creating a PR, review your own diff: | ||
|
|
||
| - [ ] Changes match the stated intent -- no unrelated modifications | ||
| - [ ] No debug code, console.logs, or TODO comments left behind | ||
| - [ ] No secrets, tokens, or credentials in the diff | ||
| - [ ] All new/changed code follows existing patterns in the codebase | ||
|
|
||
| ## Description Quality Rules | ||
|
|
||
| - Description MUST explain **what** changed and **why** | ||
| - Never leave the description blank or restate the PR title | ||
| - Use bullet points for multiple discrete changes | ||
| - Link related issues with "Resolves #NNN" or "Fixes #NNN" | ||
| - Keep the "DESCRIBE YOUR PR" section concise but informative | ||
|
|
||
| ## PR Title Format | ||
|
|
||
| PR titles follow commit convention: `type(scope): subject` | ||
|
|
||
| - Types: feat, fix, docs, ref, chore, test, style, perf, build, ci, meta | ||
| - Example: `docs(javascript): add Hono framework guide` | ||
|
|
||
| ## PR Template | ||
|
|
||
| When creating pull requests for sentry-docs, use the following format: | ||
|
|
||
| ```markdown | ||
| ## DESCRIBE YOUR PR | ||
|
|
||
| [Clear description of what the PR does and why] | ||
|
|
||
| [Bullet points of specific changes if helpful] | ||
|
|
||
| - Change 1 | ||
| - Change 2 | ||
|
|
||
| ## IS YOUR CHANGE URGENT? | ||
|
|
||
| Help us prioritize incoming PRs by letting us know when the change needs to go live. | ||
| - [ ] Urgent deadline (GA date, etc.): <!-- ENTER DATE HERE --> | ||
| - [ ] Other deadline: <!-- ENTER DATE HERE --> | ||
| - [x] None: Not urgent, can wait up to 1 week+ | ||
|
|
||
| ## SLA | ||
|
|
||
| - Teamwork makes the dream work, so please add a reviewer to your PRs. | ||
| - Please give the docs team up to 1 week to review your PR unless you've added an urgent due date to it. | ||
| Thanks in advance for your help! | ||
|
|
||
| ## PRE-MERGE CHECKLIST | ||
|
|
||
| *Make sure you've checked the following before merging your changes:* | ||
|
|
||
| - [ ] Checked Vercel preview for correctness, including links | ||
| - [ ] PR was reviewed and approved by any necessary SMEs (subject matter experts) | ||
| - [ ] PR was reviewed and approved by a member of the [Sentry docs team](https://github.com/orgs/getsentry/teams/docs) | ||
| ``` | ||
|
|
||
| ## Notes | ||
|
|
||
| - Default urgency to "None" unless the user specifies otherwise | ||
| - Include preview URLs when relevant (Vercel deploys preview URLs automatically) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| name: PR Lint | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, edited, synchronize] | ||
| branches: [master] | ||
|
|
||
| jobs: | ||
| check-pr-lint: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v3 | ||
|
|
||
| - name: Install bun | ||
| uses: oven-sh/setup-bun@v2 | ||
| with: | ||
| bun-version: latest | ||
|
|
||
| - name: Validate PR title and description | ||
| run: bun scripts/check-pr-lint.ts | ||
| env: | ||
| PR_TITLE: ${{ github.event.pull_request.title }} | ||
| PR_BODY: ${{ github.event.pull_request.body }} | ||
| PR_AUTHOR: ${{ github.event.pull_request.user.login }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| /* eslint-disable no-console */ | ||
|
|
||
| const VALID_TYPES = [ | ||
| 'feat', | ||
| 'fix', | ||
| 'docs', | ||
| 'ref', | ||
| 'chore', | ||
| 'test', | ||
| 'style', | ||
| 'perf', | ||
| 'build', | ||
| 'ci', | ||
| 'meta', | ||
| ]; | ||
|
|
||
| const TITLE_REGEX = new RegExp(`^(${VALID_TYPES.join('|')})(\\(.+\\))?: .{1,70}$`); | ||
|
|
||
| // Bot accounts whose PRs should skip lint checks | ||
| const BOT_SKIP_PREFIXES = ['dependabot', 'getsantry']; | ||
|
|
||
| // Template boilerplate lines to strip before measuring description length | ||
| const BOILERPLATE_PATTERNS = [ | ||
| /^## DESCRIBE YOUR PR/i, | ||
| /^## IS YOUR CHANGE URGENT/i, | ||
| /^## SLA/i, | ||
| /^## PRE-MERGE CHECKLIST/i, | ||
| /^## LEGAL BOILERPLATE/i, | ||
| /^## EXTRA RESOURCES/i, | ||
| /^\*.*\*/, | ||
| /^- \[ ?\]/, | ||
| /^- \[x\]/i, | ||
| /^<!--/, | ||
| /^Help us prioritize/i, | ||
| /^Teamwork makes the dream work/i, | ||
| /^Please give the docs team/i, | ||
| /^Thanks in advance/i, | ||
| /^Look, I get it/i, | ||
| /^\[Sentry Docs contributor guide\]/i, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Boilerplate patterns miss bullet-prefixed template linesHigh Severity Three boilerplate patterns use Additional Locations (1) |
||
| /^Tell us what you're changing/i, | ||
| /^\[Clear description of what the PR does/i, | ||
| /^\[Bullet points of specific changes/i, | ||
| /^- Change \d+/, | ||
| /^Urgent deadline/i, | ||
| /^Other deadline/i, | ||
| /^None: Not urgent/i, | ||
| /^Checked Vercel preview/i, | ||
| /^PR was reviewed/i, | ||
| /^Make sure you've checked/i, | ||
| /^Use this checklist/i, | ||
| /^<!-- ENTER DATE HERE -->/, | ||
| /^the entity doing business/i, | ||
| ]; | ||
|
|
||
| function stripBoilerplate(body: string): string { | ||
| return body | ||
| .split('\n') | ||
| .map(line => line.trim()) | ||
| .filter(line => { | ||
| if (line === '') return false; | ||
| if (line.startsWith('#')) return false; | ||
| return !BOILERPLATE_PATTERNS.some(pattern => pattern.test(line)); | ||
| }) | ||
| .join('\n') | ||
| .trim(); | ||
| } | ||
|
|
||
| function validateTitle(title: string): string[] { | ||
| const errors: string[] = []; | ||
|
|
||
| if (!title || title.trim() === '') { | ||
| errors.push('PR title is empty.'); | ||
| return errors; | ||
| } | ||
|
|
||
| if (!TITLE_REGEX.test(title.trim())) { | ||
| errors.push( | ||
| `PR title does not follow commit convention: type(scope): subject\n` + | ||
| ` Got: "${title}"\n` + | ||
| ` Valid types: ${VALID_TYPES.join(', ')}\n` + | ||
| ` Example: "docs(javascript): add Hono framework guide"` | ||
| ); | ||
| } | ||
|
|
||
| if (title.trim().endsWith('.')) { | ||
| errors.push('PR title should not end with a period.'); | ||
| } | ||
|
|
||
| return errors; | ||
| } | ||
|
|
||
| function validateBody(body: string, title: string): string[] { | ||
| const errors: string[] = []; | ||
|
|
||
| if (!body || body.trim() === '') { | ||
| errors.push( | ||
| 'PR description is empty. Explain what changed and why in the "DESCRIBE YOUR PR" section.' | ||
| ); | ||
| return errors; | ||
| } | ||
|
|
||
| const meaningful = stripBoilerplate(body); | ||
|
|
||
| if (meaningful.length < 20) { | ||
| errors.push( | ||
| `PR description is too short after stripping template boilerplate (${meaningful.length} chars).\n` + | ||
| ` Add a meaningful explanation of what changed and why in the "DESCRIBE YOUR PR" section.` | ||
| ); | ||
| } | ||
|
|
||
| // Check if description just restates the title | ||
| const titleWords = title | ||
| .replace(/^(feat|fix|docs|ref|chore|test|style|perf|build|ci|meta)(\(.+\))?: /i, '') | ||
| .toLowerCase() | ||
| .trim(); | ||
|
|
||
| if (titleWords && meaningful.toLowerCase().trim() === titleWords) { | ||
| errors.push( | ||
| 'PR description just restates the title. Explain *why* the change was made.' | ||
| ); | ||
| } | ||
|
|
||
| return errors; | ||
| } | ||
|
|
||
| // --- Main --- | ||
|
|
||
| const title = process.env.PR_TITLE ?? ''; | ||
| const body = process.env.PR_BODY ?? ''; | ||
| const prAuthor = process.env.PR_AUTHOR ?? ''; | ||
|
|
||
| // Skip lint for bot-authored PRs (dependabot, getsantry, etc.) | ||
| // GitHub may format bot logins as "app/getsantry" or "dependabot[bot]" | ||
| const normalizedAuthor = prAuthor | ||
| .toLowerCase() | ||
| .replace(/^app\//, '') | ||
| .replace(/\[bot\]$/, ''); | ||
|
|
||
| if (BOT_SKIP_PREFIXES.some(prefix => normalizedAuthor.startsWith(prefix))) { | ||
| console.log(`PR lint skipped for bot author: ${prAuthor}`); | ||
| process.exit(0); | ||
| } | ||
|
|
||
| const titleErrors = validateTitle(title); | ||
| const bodyErrors = validateBody(body, title); | ||
| const allErrors = [...titleErrors, ...bodyErrors]; | ||
|
|
||
| if (allErrors.length > 0) { | ||
| console.error('PR lint failed:\n'); | ||
| allErrors.forEach(err => console.error(` - ${err}\n`)); | ||
| console.error('See commit convention: https://develop.sentry.dev/commit-messages/'); | ||
| process.exit(1); | ||
| } else { | ||
| console.log('PR lint passed.'); | ||
| } | ||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Header boilerplate patterns are unreachable dead code
Low Severity
The
line.startsWith('#')check on line 61 filters out all markdown headers beforeBOILERPLATE_PATTERNSis consulted. This makes the six header-specific patterns (lines 23–29 like/^## DESCRIBE YOUR PR/i) completely unreachable dead code, since they all start with#and are already eliminated by the earlier check.Additional Locations (1)
scripts/check-pr-lint.ts#L60-L61