Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/rules/contribution-guidelines.md
42 changes: 0 additions & 42 deletions .claude/rules/pr-description.md

This file was deleted.

1 change: 1 addition & 0 deletions .claude/rules/pr-description.md
6 changes: 6 additions & 0 deletions .cursor/rules/contribution-guidelines.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
72 changes: 72 additions & 0 deletions .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)
25 changes: 25 additions & 0 deletions .github/workflows/pr-lint.yml
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 }}
21 changes: 14 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <noreply@anthropic.com>
```
## 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 <noreply@anthropic.com>`

## Development
- `yarn dev` - Start dev server with Sentry sidecar
Expand Down Expand Up @@ -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
155 changes: 155 additions & 0 deletions scripts/check-pr-lint.ts
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,
Copy link

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 before BOILERPLATE_PATTERNS is 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)

Fix in Cursor Fix in Web

/^\*.*\*/,
/^- \[ ?\]/,
/^- \[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,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boilerplate patterns miss bullet-prefixed template lines

High Severity

Three boilerplate patterns use ^ anchors that don't account for the - bullet prefix in the actual PR template. The patterns /^Teamwork makes the dream work/i, /^Please give the docs team/i, and /^\[Sentry Docs contributor guide\]/i never match because the corresponding template lines start with - (e.g. - Teamwork makes the dream work…). These ~240 chars of unstripped boilerplate easily exceed the 20-char minimum, so a PR submitted with the default template and no actual description passes the lint check, defeating its purpose.

Additional Locations (1)

Fix in Cursor Fix in Web

/^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.');
}
Loading