PR Automation with Claude CLI
A simple Docker-based automation service that receives Bitbucket pull request webhooks, clones/validates the repository, and processes them using Claude CLI (not the API).
Features
- ๐ Receives Bitbucket PR creation webhooks
- ๐ Webhook signature validation & workspace restriction (xriopteam)
- ๐ฆ Automatically clones repositories if not already present
- ๐ Updates existing repositories before processing
- ๐ค Processes PR data with Claude CLI (
--dangerously-skip-permissions) - ๐ณ Fully containerized with Docker
- โก Express.js REST API
- ๐ Easy configuration with environment variables
- ๐ Prometheus metrics integration for monitoring
Prerequisites
- Docker and Docker Compose installed
- Bitbucket repository with webhook access
- Bitbucket credentials (App Password token and username)
Note: This uses Claude CLI (installed globally in Docker), not the Anthropic API, so you don't need an API key!
Quick Start
๐ For complete setup instructions, see SETUP_GUIDE.md
Quick Start Commands
# Interactive setup (recommended)
npm run setup
# Or start manually after configuration
docker-compose up -d
What You Need
- โ Docker and Docker Compose installed
- โ Bitbucket repository with webhook access
- โ
Claude CLI installed globally:
npm install -g @anthropic-ai/claude-code
Configure Bitbucket Webhook
- Go to your Bitbucket repository settings
- Navigate to Webhooks section
- Click Add webhook
- Configure:
- Title: PR Automation
- URL:
http://your-server:3000/webhook/bitbucket/pr - Status: Active
- Triggers: Select "Pull Request" โ "Created"
- Save the webhook
How It Works
Workflow
- Webhook Received: Bitbucket sends a webhook when a PR is created
- Project Validation: The system checks if the repository is cloned in
/app/projects- If not cloned: Clones the repository from Bitbucket
- If already exists: Updates the repository (git pull)
- Claude CLI Processing: Executes
claude --dangerously-skip-permissionswith the prompt- Runs in the project directory with terminal access
- Can execute git commands, read files, analyze code
- Outputs text-based review
- Response: Claude's analysis is logged (can be extended to post comments, etc.)
Claude CLI vs API
This implementation uses Claude CLI instead of the Anthropic API:
| Feature | Claude CLI | Anthropic API |
|---|---|---|
| Authentication | Uses CLI session (no API key needed) | Requires ANTHROPIC_API_KEY |
| Capabilities | Full terminal access, can run commands | Text-only, no command execution |
| Installation | npm install -g @anthropic-ai/claude-code | npm install @anthropic-ai/sdk |
| Automation | Uses --dangerously-skip-permissions | Direct API calls |
| Cost | Free (uses Claude CLI session) | Pay per token |
Z.ai / GLM Support
You can also use Z.ai's GLM models (compatible with Claude Code) instead of Anthropic's models.
- Setup: Run
npm run setupand choose "GLM Model". - Manual Configuration:
- Get API key from Z.ai Model API.
- Set
ANTHROPIC_AUTH_TOKEN(your Z.ai key) andANTHROPIC_BASE_URL=https://api.z.ai/api/anthropicin your environment or.claude/settings.json.
Project Structure
@pr-automation/
โโโ src/
โ โโโ index.js # Express server and webhook handler
โ โโโ claude.js # Claude CLI integration with validation
โ โโโ git.js # Git operations (clone, update, validate)
โ โโโ metrics.js # Prometheus metrics collection
โ โโโ logger.js # Logging configuration
โ โโโ template-manager.js # Template management for PR reviews
โโโ tests/ # Unit tests directory
โ โโโ claude.test.js # Tests for Claude.js functionality
โ โโโ git.test.js # Tests for Git operations
โ โโโ metrics.test.js # Tests for metrics collection
โโโ projects/ # Cloned repositories (volume mounted)
โโโ Dockerfile # Docker image with Claude CLI installed
โโโ docker-compose.yml # Docker Compose setup
โโโ jest.config.json # Jest testing configuration
โโโ package.json # Node.js dependencies and scripts
โโโ .env.example # Environment variables template
โโโ README.md # This file
API Endpoints
Health Check
GET /health
Returns the service status.
Response:
{
"status": "ok",
"message": "PR Automation service is running"
}
Bitbucket PR Webhook
POST /webhook/bitbucket/pr
Receives Bitbucket pull request creation webhooks.
Expected Headers:
x-event-key: Should bepullrequest:created
Response:
{
"message": "Webhook received successfully",
"prTitle": "Add new feature"
}
Customizing PR Review Templates
The system supports modular templates for customizing review behavior without code changes.
Quick Template Setup
1. Create a custom template:
touch src/templates/custom/my-review.md
2. Write your template with variables:
**Role:** You are a security-focused code reviewer.
**Goal:** Review {{repository}} for vulnerabilities.
**PR:** `{{prUrl}}`
## Security Checklist
- Check for SQL injection
- Verify input validation
- Review authentication logic
## Final Step: Output Metrics
```json
{"isLgtm": true/false, "issueCount": 0}
3. Map repository to template:
// src/config/template-config.json
{
"defaultTemplate": "default",
"repositories": {
"payment-api": "my-review"
}
}
4. Restart service:
docker-compose restart pr-automation
Available Variables
Use these in your templates: {{prUrl}}, {{title}}, {{author}}, {{repository}}, {{sourceBranch}}, {{destinationBranch}}, {{description}}
Built-in Example Templates
security-focused- Security vulnerability analysisperformance-review- Performance bottleneck detectionquick-review- Fast review for small changes
Complete Documentation
๐ See TEMPLATE_GUIDE.md.
Testing
This project includes comprehensive unit tests to ensure code quality and reliability.
Running Tests
# Install dependencies
npm install
# Run all tests
npm test
# Run tests in watch mode (auto-reruns on file changes)
npm run test:watch
# Run tests with coverage report
npm run test:coverage
Development
Running without Docker
# Install Claude CLI globally
npm install -g @anthropic-ai/claude-code
# Install dependencies
npm install
# Run tests to verify setup
npm test
# Create projects directory
mkdir projects
# Start in development mode with auto-reload
npm run dev
Running with Docker (Development)
The docker-compose.yml includes volume mounts for hot-reloading:
docker-compose up
Claude CLI Command
The system executes Claude CLI like this:
claude --dangerously-skip-permissions \
-p "$(cat prompt.txt)" \
--model "sonnet" \
--output-format text
Flags Explained:
--dangerously-skip-permissions: Skip interactive approval prompts (required for automation)-p: Provide prompt from file--model: Choose model (haiku, sonnet, opus)--output-format text: Get plain text output
Git Operations
The system automatically handles git operations:
- Clone: If repository doesn't exist, clones from Bitbucket
- Update: If repository exists, pulls latest changes
- Authentication: Uses token and username from environment variables
Supported Authentication Method
App Password (Token + User):
BITBUCKET_USER=your-username
BITBUCKET_TOKEN=your-token-here
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
CLAUDE_MODEL | No | sonnet | Claude model: haiku, sonnet, opus or GLM models (e.g., glm-4.6) |
BITBUCKET_TOKEN | Yes | - | Bitbucket App Password or Token |
BITBUCKET_USER | Yes | - | Bitbucket username |
BITBUCKET_WEBHOOK_SECRET | Recommended | - | Webhook signature validation secret |
ALLOWED_WORKSPACE | No | xriopteam | Bitbucket workspace/organization slug to accept webhooks from |
ALLOWED_USERS | No | - | Comma-separated list of Bitbucket display names to review (e.g. "John Doe, Jane Smith"). If empty, reviews everyone. |
PROCESS_ONLY_CREATED | No | false | Set to true to only process PR creation events (ignore updates) |
PORT | No | 3000 | Server port |
METRICS_PERSISTENCE_ENABLED | No | false | Enable metrics persistence to survive restarts/rebuilds |
METRICS_PERSISTENCE_TYPE | No | filesystem | Storage type: filesystem or sqlite |
METRICS_PERSISTENCE_PATH | No | ./metrics-storage | Path to store metrics data |
METRICS_PERSISTENCE_SAVE_INTERVAL_MS | No | 30000 | Save interval in milliseconds (30 seconds) |
Troubleshooting
Check if service is running
curl http://localhost:3000/health
View logs
docker-compose logs -f pr-automation
Test Claude CLI in container
docker-compose exec pr-automation sh
claude --help
Check cloned projects
docker-compose exec pr-automation ls -la /app/projects
Test git clone manually
docker-compose exec pr-automation sh
cd /app/projects
git clone https://x-token-auth:YOUR_TOKEN@bitbucket.org/your-workspace/your-repo.git
Restart service
docker-compose restart
Rebuild after changes
docker-compose down
docker-compose build --no-cache
docker-compose up -d
Stop service
docker-compose down
Clear all projects (reset)
rm -rf projects/*
docker-compose restart
Webhook Security
The webhook endpoint is secured with two layers of protection:
1. Signature Validation
All webhook requests must include a valid HMAC-SHA256 signature in the X-Hub-Signature header. This ensures requests actually come from Bitbucket.
2. Workspace Restriction
Only webhooks from the xriopteam Bitbucket workspace are accepted. This prevents unauthorized access from other organizations.
Setup
-
Generate a webhook secret:
openssl rand -hex 32 -
Add to
.envfile:BITBUCKET_WEBHOOK_SECRET=your-generated-secret ALLOWED_WORKSPACE=xriopteam -
Configure in Bitbucket:
- Go to Repository Settings โ Webhooks
- Add webhook URL:
https://bitbucket.tintinwinata.online/webhook/bitbucket/pr - Add the same secret in the "Secret" field
- Select triggers: PR Created, PR Updated
-
Restart service:
docker compose restart pr-automation
๐ See WEBHOOK_SECURITY.md for detailed configuration and troubleshooting.
Monitoring with Prometheus
The application exposes Prometheus metrics at /metrics endpoint for monitoring PR automation activities and Claude review performance.
Available Metrics
- PR Created:
pr_created_total- Number of PRs created - PR Updated:
pr_updated_total- Number of PRs updated - LGTM Count:
claude_lgtm_total- Number of approvals from Claude - Issues Found:
claude_issues_found_total- Total count of all issues found (e.g., if 1 PR has 3 issues, adds 3 to counter) - Successful Reviews:
claude_review_success_total- PRs successfully reviewed - Failed Reviews:
claude_review_failure_total- Failed reviews (with error types) - Review Duration:
claude_review_duration_seconds- Histogram of review durations
Access Metrics
curl http://localhost:3000/metrics
Detailed Documentation
See PROMETHEUS.md for:
- Detailed metric descriptions
- Grafana dashboard examples
- Sample PromQL queries
Note: Prometheus is already configured in /workspace/monitoring/prometheus.yml to scrape metrics from pr-automation:3000.
Metrics Persistence
By default, metrics are stored in memory and reset when the application restarts. You can enable metrics persistence to preserve metrics across restarts and container rebuilds.
Enable Metrics Persistence
Add these environment variables to your .env file:
METRICS_PERSISTENCE_ENABLED=true
METRICS_PERSISTENCE_TYPE=filesystem
METRICS_PERSISTENCE_PATH=./metrics-storage
METRICS_PERSISTENCE_SAVE_INTERVAL_MS=30000
Storage Types
Filesystem (Recommended for most use cases)
- Stores metrics in a JSON file
- Simple and easy to inspect
- Works well for small to medium deployments
- Default storage type
SQLite (Recommended for larger deployments)
- Stores metrics in a SQLite database
- Better performance for high-volume metrics
- Requires
better-sqlite3package (automatically installed) - Falls back to filesystem if SQLite is unavailable
Configuration Options
| Option | Description | Default |
|---|---|---|
METRICS_PERSISTENCE_ENABLED | Enable/disable persistence | false |
METRICS_PERSISTENCE_TYPE | Storage type: filesystem or sqlite | filesystem |
METRICS_PERSISTENCE_PATH | Path to store metrics (relative or absolute) | ./metrics-storage |
METRICS_PERSISTENCE_SAVE_INTERVAL_MS | How often to save metrics (milliseconds) | 30000 (30 seconds) |
Docker Setup
When using Docker, make sure to mount the metrics storage directory as a volume:
volumes:
- ./metrics-storage:/app/metrics-storage
This ensures metrics persist even when the container is rebuilt.
How It Works
- On Startup: The application loads persisted metrics from storage and restores them to the Prometheus registry
- During Runtime: Metrics are automatically saved every 30 seconds (configurable via
METRICS_PERSISTENCE_SAVE_INTERVAL_MS) - On Shutdown: Metrics are saved one final time before the process exits
Backward Compatibility
- Metrics persistence is opt-in - disabled by default
- If persistence fails to initialize, the application continues without persistence (logs a warning)
- Existing deployments without persistence continue to work as before
Troubleshooting
Metrics not persisting:
- Check that
METRICS_PERSISTENCE_ENABLED=trueis set - Verify the storage path is writable
- Check application logs for persistence-related errors
Permission errors:
- Ensure the storage directory exists and is writable
- In Docker, verify volume mounts are configured correctly
Contributing
Feel free to contribute to this project! Whether you want to:
- ๐ Report bugs or issues
- ๐ก Suggest new features or improvements
- ๐ง Submit pull requests with fixes or enhancements
- ๐ Improve documentation or examples
- ๐งช Add tests or improve existing ones
Getting Started
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name - Make your changes and test them thoroughly
- Commit your changes:
git commit -m "Add your feature" - Push to your fork:
git push origin feature/your-feature-name - Open a Pull Request
Questions or Discussion?
I'm always open to discussing issues, reviewing PRs, or just chatting about the project!
Feel free to DM me on LinkedIn - I'd love to hear from you and help with any questions you might have.
Happy coding! ๐