Skip to content
Draft
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
3 changes: 2 additions & 1 deletion .cursor/rules/sentry-cli-project.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ This is **sentry-cli**, a command-line utility for working with Sentry. It's pri

- `src/` - Core Rust source code with command modules and utilities
- `js/` - JavaScript wrapper and npm package code
- `scripts/` - Build and utility scripts
- `scripts/` - Build, installation, and deployment scripts
- `tools/` - Development tooling and utilities (commit hooks, validation scripts)
- `tests/integration/` - Integration tests using `.trycmd` format
- `npm-binary-distributions/` - Platform-specific binary packages
- `.github/workflows/` - CI/CD workflows (follows reusable workflow pattern)
Expand Down
13 changes: 13 additions & 0 deletions .gitmessage
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

# Commit Message Format
# <type>(<scope>): <subject>
#
# <body - explain what and why, not how>
#
# <footer - link issues: "Fixes #123", breaking: "BREAKING CHANGE: desc">
#
# Example: feat(api): Add user authentication endpoint
# Types: feat, fix, docs, style, ref, test, ci, build, perf, meta, license, revert
# Subject: Capitalize, imperative mood, no period, max 70 chars
# Body: Separate from subject with blank line
# Details: https://develop.sentry.dev/engineering-practices/commit-messages/
27 changes: 27 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# pre-commit configuration for sentry-cli
# See https://pre-commit.com for more information
default_stages: [pre-commit]
fail_fast: false

repos:
# Commit message validation
- repo: local
hooks:
- id: validate-commit-msg
name: Validate commit message
entry: ./tools/validate-commit-msg.py
language: system
stages: [commit-msg]
always_run: true

# Standard hooks for code quality
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: check-merge-conflict
- id: mixed-line-ending
args: [--fix=lf]
8 changes: 8 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# Pre-commit hooks

Pre-commit hooks provide some local validation of your commits. Please set up the hooks with the following script:

```bash
./tools/setup-commit-hooks.sh
```

# Adding new commands
For new commands, it is recommended to use clap's [Derive API](https://docs.rs/clap/latest/clap/_derive/index.html).
In contrast to the [Builder API](https://docs.rs/clap/latest/clap/_tutorial/index.html), the Derive API makes it:
Expand Down
38 changes: 38 additions & 0 deletions tools/setup-commit-hooks.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/bash
# Script to set up commit message validation hooks for sentry-cli

set -e

echo "Setting up commit hooks for sentry-cli..."

# Check if pre-commit is installed
if ! command -v pre-commit &> /dev/null; then
echo "❌ pre-commit is not installed."
echo ""
echo "Please install pre-commit using one of these methods:"
echo " - pip install pre-commit"
echo " - brew install pre-commit (macOS)"
echo " - pipx install pre-commit"
echo ""
echo "Then run this script again."
exit 1
fi

# Install the git hooks
echo "Installing pre-commit hooks..."
pre-commit install
pre-commit install --hook-type commit-msg

# Always set up the commit message template
echo "Setting up commit message template..."
git config commit.template .gitmessage
echo "✅ Commit message template configured"

echo ""
echo "✅ Setup complete!"
echo ""
echo "Your commits will now be validated against Sentry's commit message format."
echo "Format: <type>(<scope>): <subject>"
echo "Example: feat(cli): Add new authentication feature"
echo ""
echo "For more details: https://develop.sentry.dev/engineering-practices/commit-messages/"
117 changes: 117 additions & 0 deletions tools/validate-commit-msg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/usr/bin/env python3
"""
Validates commit messages according to Sentry's commit message guidelines.
https://develop.sentry.dev/engineering-practices/commit-messages/
"""

import re
import sys


def validate_commit_message(message):
"""
Validates a commit message according to Sentry's format:
<type>(<scope>): <subject>

Returns tuple of (is_valid, error_message)
"""
# Valid commit types
valid_types = [
"build",
"ci",
"docs",
"feat",
"fix",
"perf",
"ref",
"style",
"test",
"meta",
"license",
"revert",
]

# Skip validation for merge commits
if message.startswith("Merge"):
return True, None

# Parse the first line (header)
lines = message.strip().split("\n")

header = lines[0].strip()

# Pattern for the header: type(scope): subject or type: subject
pattern = r"^(?P<type>[a-z]+)(?:\((?P<scope>[^)]+)\))?: (?P<subject>.+)$"
match = re.match(pattern, header)

if not match:
return (
False,
"Invalid format. Must be: <type>(<scope>): <subject> or <type>: <subject>",
)

commit_type = match.group("type")
scope = match.group("scope")
subject = match.group("subject")

# Validate type
if commit_type not in valid_types:
return (
False,
f"Invalid type '{commit_type}'. Must be one of: {', '.join(valid_types)}",
)

# Validate scope (if present)
if scope and not scope.islower():
return False, f"Scope '{scope}' must be lowercase"

# Validate subject
if not subject:
return False, "Subject cannot be empty"

# Check first letter is capitalized
if subject[0].islower():
return False, "Subject must start with a capital letter"

# Check for trailing period
if subject.endswith("."):
return False, "Subject must not end with a period"

# Check header length (max 70 characters)
if len(header) > 70:
return False, f"Header is {len(header)} characters, must be 70 or less"

return True, None


def main():
"""Main entry point for the commit message validator."""
# Read commit message from file (provided by git)
if len(sys.argv) < 2:
print("Error: No commit message file provided")
sys.exit(1)

commit_msg_file = sys.argv[1]

try:
with open(commit_msg_file, "r", encoding="utf-8") as f:
commit_message = f.read()
except Exception as e:
print(f"Error reading commit message file: {e}")
sys.exit(1)

# Validate the commit message
is_valid, error_msg = validate_commit_message(commit_message)

if not is_valid:
print(f"❌ Commit message validation failed:\n{error_msg}")
print("\nCommit message format: <type>(<scope>): <subject>")
print("Example: feat(api): Add new authentication endpoint")
print(
"\nSee https://develop.sentry.dev/engineering-practices/commit-messages/ for details"
)
sys.exit(1)


if __name__ == "__main__":
main()