From eafcb2c2e24e759755e8f3dcdb8d819c74fef7b1 Mon Sep 17 00:00:00 2001 From: Sergiy Dybskiy Date: Thu, 12 Feb 2026 16:30:55 -0500 Subject: [PATCH 1/2] ci(pr-lint): Enforce Sentry commit convention and PR description quality Add GHA workflow + script to validate PR titles match `type(scope): subject` and descriptions are meaningful (not blank/boilerplate). Bot authors (dependabot, getsantry) are auto-skipped. Unify agent rules so .claude/rules/ symlinks to .cursor/rules/ as single source of truth. Co-Authored-By: Claude --- .claude/rules/contribution-guidelines.md | 1 + .claude/rules/pr-description.md | 43 +----- .cursor/rules/contribution-guidelines.mdc | 6 + .cursor/rules/pr-description.mdc | 72 ++++++++++ .github/workflows/pr-lint.yml | 25 ++++ AGENTS.md | 21 ++- scripts/check-pr-lint.ts | 155 ++++++++++++++++++++++ 7 files changed, 274 insertions(+), 49 deletions(-) create mode 120000 .claude/rules/contribution-guidelines.md mode change 100644 => 120000 .claude/rules/pr-description.md create mode 100644 .cursor/rules/pr-description.mdc create mode 100644 .github/workflows/pr-lint.yml create mode 100644 scripts/check-pr-lint.ts diff --git a/.claude/rules/contribution-guidelines.md b/.claude/rules/contribution-guidelines.md new file mode 120000 index 00000000000000..20efdbc0c01000 --- /dev/null +++ b/.claude/rules/contribution-guidelines.md @@ -0,0 +1 @@ +../../.cursor/rules/contribution-guidelines.mdc \ No newline at end of file diff --git a/.claude/rules/pr-description.md b/.claude/rules/pr-description.md deleted file mode 100644 index 0abf849db386f8..00000000000000 --- a/.claude/rules/pr-description.md +++ /dev/null @@ -1,42 +0,0 @@ -# PR Description Format - -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.): -- [ ] Other deadline: -- [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) -- Keep the "DESCRIBE YOUR PR" section concise but informative -- Use bullet points for multiple discrete changes diff --git a/.claude/rules/pr-description.md b/.claude/rules/pr-description.md new file mode 120000 index 00000000000000..8a466d2e71e0a5 --- /dev/null +++ b/.claude/rules/pr-description.md @@ -0,0 +1 @@ +../../.cursor/rules/pr-description.mdc \ No newline at end of file diff --git a/.cursor/rules/contribution-guidelines.mdc b/.cursor/rules/contribution-guidelines.mdc index 031e738e20421c..a3351cd6db4602 100644 --- a/.cursor/rules/contribution-guidelines.mdc +++ b/.cursor/rules/contribution-guidelines.mdc @@ -9,6 +9,12 @@ alwaysApply: false This is a Next.js documentation site using MDX for content management. The codebase serves SDK documentation for multiple platforms and frameworks with a sophisticated shared content system. +## Commit Convention + +All commits follow `type(scope): subject` per https://develop.sentry.dev/commit-messages/. +Types: feat, fix, docs, ref, chore, test, style, perf, build, ci, meta. +PR titles use the same format. + ## File Structure & Organization ### Core Directories diff --git a/.cursor/rules/pr-description.mdc b/.cursor/rules/pr-description.mdc new file mode 100644 index 00000000000000..700a5b8be1739a --- /dev/null +++ b/.cursor/rules/pr-description.mdc @@ -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.): +- [ ] Other deadline: +- [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) diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml new file mode 100644 index 00000000000000..3959731f379fe9 --- /dev/null +++ b/.github/workflows/pr-lint.yml @@ -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 }} diff --git a/AGENTS.md b/AGENTS.md index 695f4aed964ffd..eac6c080ca8759 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,11 +3,14 @@ ## Package Manager Use **yarn**: `yarn install`, `yarn dev`, `yarn build`, `yarn test` -## Commit Attribution -AI commits MUST include: -``` -Co-Authored-By: Claude -``` +## Commits + +Format: `type(scope): subject` per https://develop.sentry.dev/commit-messages/ + +- Types: feat, fix, docs, ref, chore, test, style, perf, build, ci, meta +- Subject: imperative, capitalized, max 70 chars, no period +- Each commit = single stable change +- AI commits MUST include: `Co-Authored-By: Claude ` ## Development - `yarn dev` - Start dev server with Sentry sidecar @@ -69,6 +72,10 @@ When writing requirements in `develop-docs/`: - Make the plan extremely concise. Sacrifice grammar for the sake of concision. - At the end of each plan, give me a list of unresolved questions to answer, if any. -## Pull Request generation +## Pull Requests -Use .github/PULL_REQUEST_TEMPLATE.md and add Co-Authored-By: Claude +1. Self-review your diff before creating the PR +2. Use .github/PULL_REQUEST_TEMPLATE.md +3. Description MUST explain what changed and why -- never blank or title restatement +4. PR title follows commit format: `type(scope): subject` +5. One feature/fix per PR diff --git a/scripts/check-pr-lint.ts b/scripts/check-pr-lint.ts new file mode 100644 index 00000000000000..4b28475cc1ae95 --- /dev/null +++ b/scripts/check-pr-lint.ts @@ -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, + /^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, + /^/, + /^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.'); +} From 35bbd913584091ace46329b381fb5230936e0472 Mon Sep 17 00:00:00 2001 From: Sergiy Dybskiy Date: Thu, 12 Feb 2026 16:34:11 -0500 Subject: [PATCH 2/2] fix(pr-lint): Simplify HTML comment regex to satisfy CodeQL CodeQL flagged /^$/ as "Bad HTML filtering regexp". Since we match line-by-line, just match lines starting with $/, + /^