Compare commits

..

1 Commits

Author SHA1 Message Date
Alexander Whitestone
d1ebfe00cf feat: Add duplicate PR prevention tools (#1474)
Some checks failed
CI / test (pull_request) Failing after 49s
CI / validate (pull_request) Failing after 46s
Review Approval Gate / verify-review (pull_request) Successful in 5s
Add pre-flight check tools to prevent duplicate PRs from being created
for the same issue. This addresses the irony of creating duplicate PRs
for issue #1128 which was about cleaning up duplicate PRs.

Changes:
- Added scripts/check-existing-prs.sh - Bash script to check for existing PRs
- Added scripts/check_existing_prs.py - Python version of the check
- Added scripts/pr-safe.sh - User-friendly wrapper with guidance
- Added docs/duplicate-pr-prevention.md - Documentation and usage guide

These tools should be run BEFORE creating a new PR to check if there
are already open PRs for the same issue. This prevents the ironic
situation of creating duplicate PRs while trying to clean up duplicate PRs.

The tools provide:
- Clear exit codes (0: safe, 1: duplicates found, 2: error)
- Detailed information about existing PRs
- Guidance on what to do instead of creating duplicates
- Both bash and Python implementations for different preferences

Closes #1474
2026-04-14 19:00:10 -04:00
8 changed files with 410 additions and 412 deletions

View File

@@ -1,70 +0,0 @@
name: PR Backlog Management
on:
schedule:
# Run weekly on Monday at 10 AM UTC
- cron: '0 10 * * 1'
workflow_dispatch: # Allow manual trigger
jobs:
analyze-backlog:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install requests
- name: Analyze PR backlog
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
python scripts/pr-backlog-analyzer.py
- name: Upload report
uses: actions/upload-artifact@v4
with:
name: pr-backlog-report
path: reports/
- name: Create issue if backlog is high
if: failure()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('reports/pr-backlog-' + new Date().toISOString().split('T')[0] + '.md', 'utf8');
// Check if backlog is high (more than 10 stale PRs)
const staleMatch = report.match(/Stale \(>30 days\): (\d+)/);
const staleCount = staleMatch ? parseInt(staleMatch[1]) : 0;
if (staleCount > 10) {
const title = 'PR Backlog Alert: ' + staleCount + ' stale PRs';
const body = `## PR Backlog Alert
The PR backlog analysis found ${staleCount} stale PRs (>30 days old).
### Recommendation
Review and close stale PRs to reduce backlog.
### Report
See attached artifact for full analysis.
This issue was automatically created by the PR backlog management workflow.`;
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title,
body,
labels: ['process-improvement', 'p2-backlog']
});
}

View File

@@ -0,0 +1,136 @@
# Duplicate PR Prevention Tools
## Problem
Despite having tools to detect and clean up duplicate PRs, agents were still creating duplicate PRs for the same issue. This was incredibly ironic, especially for issue #1128 which was about cleaning up duplicate PRs.
## Solution
We've created pre-flight check tools that agents should run **before** creating a new PR. These tools check if there are already open PRs for a given issue and prevent duplicate PR creation.
## Tools
### 1. `check-existing-prs.sh` (Bash)
Check if PRs already exist for an issue.
```bash
# Check for existing PRs for issue #1128
./scripts/check-existing-prs.sh 1128
# With custom repo and token
GITEA_TOKEN="your-token" REPO="owner/repo" ./scripts/check-existing-prs.sh 1128
```
**Exit codes:**
- `0`: No existing PRs found (safe to create new PR)
- `1`: Existing PRs found (do not create new PR)
- `2`: Error (API failure, missing parameters, etc.)
### 2. `check_existing_prs.py` (Python)
Python version of the check, suitable for agents that prefer Python.
```bash
# Check for existing PRs for issue #1128
python3 scripts/check_existing_prs.py 1128
# With custom repo and token
python3 scripts/check_existing_prs.py 1128 "owner/repo" "your-token"
```
### 3. `pr-safe.sh` (Wrapper)
User-friendly wrapper that provides guidance.
```bash
# Check and get suggestions
./scripts/pr-safe.sh 1128
# With suggested branch name
./scripts/pr-safe.sh 1128 fix/1128-my-fix
```
## Workflow Integration
### For Agents
Before creating a PR, agents should:
1. Run the check: `./scripts/check-existing-prs.sh <issue_number>`
2. If exit code is `0`, proceed with PR creation
3. If exit code is `1`, review existing PRs instead
### For Humans
Before creating a PR:
1. Run: `./scripts/pr-safe.sh <issue_number>`
2. Follow the guidance provided
## Prevention Strategy
### 1. Pre-flight Checks
Always run a pre-flight check before creating a PR:
```bash
# In your agent workflow
if ./scripts/check-existing-prs.sh $ISSUE_NUMBER; then
# Safe to create PR
create_pr
else
# Don't create PR, review existing ones
review_existing_prs
fi
```
### 2. GitHub Actions Integration
The existing `.github/workflows/pr-duplicate-check.yml` workflow can be enhanced to run these checks automatically.
### 3. Agent Instructions
Add to agent instructions:
```
Before creating a PR for an issue, ALWAYS run:
./scripts/check-existing-prs.sh <issue_number>
If PRs already exist, DO NOT create a new PR.
Instead, review existing PRs and add comments or merge them.
```
## Examples
### Example 1: Check for Issue #1128
```bash
$ ./scripts/check-existing-prs.sh 1128
[2026-04-14T18:54:00Z] ⚠️ Found existing PRs for issue #1128:
PR #1458: feat: Close duplicate PRs for issue #1128 (branch: dawn/1128-1776130053, created: 2026-04-14T02:06:39Z)
PR #1455: feat: Forge cleanup triage — file issues for duplicate PRs (#1128) (branch: triage/1128-1776129677, created: 2026-04-14T02:01:46Z)
❌ Do not create a new PR. Review existing PRs first.
```
### Example 2: Safe to Create PR
```bash
$ ./scripts/check-existing-prs.sh 9999
[2026-04-14T18:54:00Z] ✅ No existing PRs found for issue #9999
Safe to create a new PR
```
## Related Issues
- Issue #1474: [META] Still creating duplicate PRs for issue #1128 despite cleanup
- Issue #1460: [META] I keep creating duplicate PRs for issue #1128
- Issue #1128: [RESOLVED] Forge Cleanup — PRs Closed, Milestones Deduplicated, Policy Issues Filed
## Lessons Learned
1. **Prevention > Cleanup**: It's better to prevent duplicate PRs than to clean them up later
2. **Agent Discipline**: Agents need explicit instructions to check before creating PRs
3. **Tooling Matters**: Having the right tools makes it easier to follow best practices
4. **Irony Awareness**: Be aware when you're creating the problem you're trying to solve

View File

@@ -1,126 +0,0 @@
# PR Backlog Management Process
## Overview
This document outlines the process for managing PR backlog in the Timmy Foundation repositories, specifically addressing the high PR backlog in timmy-config.
## Current State
As of the latest analysis:
- **timmy-config**: 31 open PRs (highest in org)
- **the-nexus**: Multiple PRs for same issues
- **hermes-agent**: Moderate PR count
## Process
### 1. Weekly Analysis
Run the PR backlog analyzer weekly:
```bash
python scripts/pr-backlog-analyzer.py
```
This generates a report in `reports/pr-backlog-YYYYMMDD.md`.
### 2. Review Stale PRs
PRs older than 30 days are considered stale. For each stale PR:
1. **Check relevance**: Is the PR still needed?
2. **Check conflicts**: Does it conflict with current main?
3. **Check activity**: Has there been recent activity?
4. **Action**: Close, update, or merge
### 3. Merge Approved PRs
PRs with approvals should be merged within 7 days:
1. **Verify CI**: Ensure all checks pass
2. **Verify review**: At least 1 approval
3. **Merge**: Use squash merge for clean history
4. **Delete branch**: Clean up after merge
### 4. Review Pending PRs
PRs waiting for review should be reviewed within 48 hours:
1. **Assign reviewer**: Ensure someone is responsible
2. **Review**: Check code quality, tests, documentation
3. **Approve or request changes**: Don't leave PRs in limbo
4. **Follow up**: If no response in 48 hours, escalate
### 5. Close Duplicate PRs
Multiple PRs for the same issue should be consolidated:
1. **Identify duplicates**: Same issue number or similar changes
2. **Keep newest**: Usually the most up-to-date
3. **Close older**: With explanatory comments
4. **Document**: Update issue with which PR was kept
## Automation
### GitHub Actions Workflow
The `pr-backlog-management.yml` workflow runs weekly to:
1. Analyze all open PRs
2. Generate a report
3. Create an issue if backlog is high (>10 stale PRs)
### Manual Trigger
The workflow can be triggered manually via GitHub Actions UI.
## Metrics
Track these metrics weekly:
- **Total open PRs**: Should be <20 per repo
- **Stale PRs**: Should be <5 per repo
- **Average PR age**: Should be <14 days
- **Time to review**: Should be <48 hours
- **Time to merge**: Should be <7 days after approval
## Escalation
If backlog exceeds thresholds:
1. **Level 1**: Automated issue created
2. **Level 2**: Team lead notified
3. **Level 3**: Organization-wide cleanup sprint
## Tools
### PR Backlog Analyzer
```bash
# Run analysis
python scripts/pr-backlog-analyzer.py
# View report
cat reports/pr-backlog-$(date +%Y%m%d).md
```
### Manual Cleanup
```bash
# List stale PRs
curl -s -H "Authorization: token $GITEA_TOKEN" "https://forge.alexanderwhitestone.com/api/v1/repos/Timmy_Foundation/timmy-config/pulls?state=open" | jq -r '.[] | select(.created_at < "'$(date -u -d '30 days ago' +%Y-%m-%dT%H:%M:%SZ)'") | .number'
# Close a PR
curl -s -X PATCH -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" -d '{"state": "closed"}' "https://forge.alexanderwhitestone.com/api/v1/repos/Timmy_Foundation/timmy-config/pulls/123"
```
## Success Criteria
- **Short-term**: Reduce timmy-config PRs from 31 to <20
- **Medium-term**: Maintain <15 open PRs across all repos
- **Long-term**: Automated PR lifecycle management
## Related
- Issue #1470: process: Address timmy-config PR backlog (9 PRs - highest in org)
- Issue #1127: Evening triage pass
- Issue #1128: Forge Cleanup

View File

@@ -1,35 +0,0 @@
# PR Backlog Report — Timmy_Foundation/timmy-config
Generated: 2026-04-14 21:13:34
## Summary
- **Total Open PRs**: 32
- **Stale (>30 days)**: 0
- **Needs Review**: 0
- **Approved**: 0
- **Changes Requested**: 0
- **Recent (<7 days)**: 32
## Recommendations
### Immediate Actions
1. **Merge approved PRs**: 0 PRs are ready to merge
2. **Review stale PRs**: 0 PRs are >30 days old
3. **Address changes requested**: 0 PRs need updates
### Process Improvements
1. **Assign reviewers**: Ensure each PR has a reviewer within 24 hours
2. **Set SLAs**:
- Review within 48 hours
- Merge within 7 days of approval
- Close stale PRs after 30 days
3. **Automate**: Add CI checks to prevent backlog
## Detailed Analysis
### Stale PRs (>30 days)
### Approved PRs (Ready to Merge)
### Needs Review

78
scripts/check-existing-prs.sh Executable file
View File

@@ -0,0 +1,78 @@
#!/usr/bin/env bash
# ═══════════════════════════════════════════════════════════════
# check-existing-prs.sh — Check if PRs already exist for an issue
#
# This script checks if there are already open PRs for a given issue
# before creating a new one. This prevents duplicate PRs.
#
# Usage:
# ./scripts/check-existing-prs.sh <issue_number>
#
# Exit codes:
# 0 - No existing PRs found (safe to create new PR)
# 1 - Existing PRs found (do not create new PR)
# 2 - Error (API failure, missing parameters, etc.)
#
# Designed for issue #1474: Prevent duplicate PRs
# ═══════════════════════════════════════════════════════════════
set -euo pipefail
# ─── Configuration ──────────────────────────────────────────
GITEA_URL="${GITEA_URL:-https://forge.alexanderwhitestone.com}"
GITEA_TOKEN="${GITEA_TOKEN:?Set GITEA_TOKEN env var}"
REPO="${REPO:-Timmy_Foundation/the-nexus}"
ISSUE_NUMBER="${1:?Usage: $0 <issue_number>}"
API="$GITEA_URL/api/v1"
AUTH="Authorization: token $GITEA_TOKEN"
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $*"; }
# ─── Validate inputs ──────────────────────────────────────
if ! [[ "$ISSUE_NUMBER" =~ ^[0-9]+$ ]]; then
log "ERROR: Issue number must be a positive integer"
exit 2
fi
# ─── Fetch open PRs ────────────────────────────────────────
log "Checking for existing PRs for issue #$ISSUE_NUMBER in $REPO"
OPEN_PRS=$(curl -s -H "$AUTH" "$API/repos/$REPO/pulls?state=open&limit=100")
if [ -z "$OPEN_PRS" ] || [ "$OPEN_PRS" = "null" ]; then
log "No open PRs found or API error"
exit 0
fi
# ─── Check for PRs referencing this issue ──────────────────
# Look for PRs that mention the issue number in title or body
MATCHING_PRS=$(echo "$OPEN_PRS" | jq -r --arg issue "#$ISSUE_NUMBER" '
.[] |
select(
(.title | test($issue; "i")) or
(.body | test($issue; "i"))
) |
"PR #\(.number): \(.title) (branch: \(.head.ref), created: \(.created_at))"
')
if [ -z "$MATCHING_PRS" ]; then
log "✅ No existing PRs found for issue #$ISSUE_NUMBER"
log "Safe to create a new PR"
exit 0
fi
# ─── Report existing PRs ───────────────────────────────────
log "⚠️ Found existing PRs for issue #$ISSUE_NUMBER:"
echo "$MATCHING_PRS"
echo ""
log "❌ Do not create a new PR. Review existing PRs first."
log ""
log "Options:"
log " 1. Review and merge an existing PR"
log " 2. Close duplicates and keep the best one"
log " 3. Add comments to existing PRs instead of creating new ones"
log ""
log "To see details of existing PRs:"
log " curl -H \"Authorization: token \$GITEA_TOKEN\" \"$API/repos/$REPO/pulls?state=open\" | jq '.[] | select(.title | test(\"#$ISSUE_NUMBER\"; \"i\"))'"
exit 1

148
scripts/check_existing_prs.py Executable file
View File

@@ -0,0 +1,148 @@
#!/usr/bin/env python3
"""
Check if PRs already exist for an issue before creating a new one.
This script prevents duplicate PRs by checking if there are already
open PRs for a given issue.
Usage:
python3 scripts/check_existing_prs.py <issue_number>
Exit codes:
0 - No existing PRs found (safe to create new PR)
1 - Existing PRs found (do not create new PR)
2 - Error (API failure, missing parameters, etc.)
Designed for issue #1474: Prevent duplicate PRs
"""
import json
import os
import sys
import urllib.request
import urllib.error
from datetime import datetime
def check_existing_prs(issue_number: int, repo: str = None, token: str = None) -> int:
"""
Check if PRs already exist for an issue.
Args:
issue_number: The issue number to check
repo: Repository in format "owner/repo" (default: from env or "Timmy_Foundation/the-nexus")
token: Gitea API token (default: from GITEA_TOKEN env var)
Returns:
0: No existing PRs found (safe to create new PR)
1: Existing PRs found (do not create new PR)
2: Error (API failure, missing parameters, etc.)
"""
# Get configuration from environment
gitea_url = os.environ.get('GITEA_URL', 'https://forge.alexanderwhitestone.com')
token = token or os.environ.get('GITEA_TOKEN')
repo = repo or os.environ.get('REPO', 'Timmy_Foundation/the-nexus')
if not token:
print("ERROR: GITEA_TOKEN environment variable not set", file=sys.stderr)
return 2
# Validate issue number
if not isinstance(issue_number, int) or issue_number <= 0:
print("ERROR: Issue number must be a positive integer", file=sys.stderr)
return 2
# Build API URL
api_url = f"{gitea_url}/api/v1/repos/{repo}/pulls?state=open&limit=100"
# Make API request
try:
req = urllib.request.Request(api_url, headers={
'Authorization': f'token {token}',
'Content-Type': 'application/json'
})
with urllib.request.urlopen(req, timeout=30) as resp:
prs = json.loads(resp.read())
except urllib.error.URLError as e:
print(f"ERROR: Failed to fetch PRs: {e}", file=sys.stderr)
return 2
except json.JSONDecodeError as e:
print(f"ERROR: Failed to parse API response: {e}", file=sys.stderr)
return 2
except Exception as e:
print(f"ERROR: Unexpected error: {e}", file=sys.stderr)
return 2
# Check for PRs referencing this issue
issue_ref = f"#{issue_number}"
matching_prs = []
for pr in prs:
title = pr.get('title', '')
body = pr.get('body', '') or ''
# Check if issue is referenced in title or body
if issue_ref in title or issue_ref in body:
matching_prs.append(pr)
# Report results
timestamp = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
if not matching_prs:
print(f"[{timestamp}] ✅ No existing PRs found for issue #{issue_number}")
print("Safe to create a new PR")
return 0
# Found existing PRs
print(f"[{timestamp}] ⚠️ Found existing PRs for issue #{issue_number}:")
print()
for pr in matching_prs:
pr_number = pr.get('number')
pr_title = pr.get('title')
pr_branch = pr.get('head', {}).get('ref', 'unknown')
pr_created = pr.get('created_at', 'unknown')
pr_url = pr.get('html_url', 'unknown')
print(f" PR #{pr_number}: {pr_title}")
print(f" Branch: {pr_branch}")
print(f" Created: {pr_created}")
print(f" URL: {pr_url}")
print()
print("❌ Do not create a new PR. Review existing PRs first.")
print()
print("Options:")
print(" 1. Review and merge an existing PR")
print(" 2. Close duplicates and keep the best one")
print(" 3. Add comments to existing PRs instead of creating new ones")
print()
print("To see details of existing PRs:")
print(f' curl -H "Authorization: token $GITEA_TOKEN" "{gitea_url}/api/v1/repos/{repo}/pulls?state=open" | jq \'.[] | select(.title | test("#{issue_number}"; "i"))\'')
return 1
def main():
"""Main entry point."""
if len(sys.argv) < 2:
print("Usage: python3 check_existing_prs.py <issue_number>", file=sys.stderr)
print(" python3 check_existing_prs.py <issue_number> [repo] [token]", file=sys.stderr)
return 2
try:
issue_number = int(sys.argv[1])
except ValueError:
print("ERROR: Issue number must be an integer", file=sys.stderr)
return 2
repo = sys.argv[2] if len(sys.argv) > 2 else None
token = sys.argv[3] if len(sys.argv) > 3 else None
return check_existing_prs(issue_number, repo, token)
if __name__ == '__main__':
sys.exit(main())

View File

@@ -1,181 +0,0 @@
#!/usr/bin/env python3
"""
PR Backlog Analyzer for timmy-config
Analyzes open PRs and provides recommendations for cleanup.
"""
import json
import subprocess
import sys
from datetime import datetime, timedelta
from pathlib import Path
def get_open_prs(repo: str, token: str) -> list:
"""Get all open PRs from a repository."""
result = subprocess.run([
"curl", "-s", "-H", f"Authorization: token {token}",
f"https://forge.alexanderwhitestone.com/api/v1/repos/{repo}/pulls?state=open&limit=100"
], capture_output=True, text=True)
if result.returncode != 0:
print(f"Error fetching PRs: {result.stderr}")
return []
return json.loads(result.stdout)
def analyze_pr(pr: dict) -> dict:
"""Analyze a single PR."""
created = datetime.fromisoformat(pr['created_at'].replace('Z', '+00:00'))
age_days = (datetime.now(created.tzinfo) - created).days
# Check for reviews
reviews = pr.get('reviews', [])
has_approvals = any(r.get('state') == 'APPROVED' for r in reviews)
has_changes_requested = any(r.get('state') == 'CHANGES_REQUESTED' for r in reviews)
# Check labels
labels = [l['name'] for l in pr.get('labels', [])]
return {
'number': pr['number'],
'title': pr['title'],
'branch': pr['head']['ref'],
'created': pr['created_at'],
'age_days': age_days,
'user': pr['user']['login'],
'has_approvals': has_approvals,
'has_changes_requested': has_changes_requested,
'labels': labels,
'url': pr['html_url'],
}
def categorize_prs(prs: list) -> dict:
"""Categorize PRs by status."""
categories = {
'stale': [], # > 30 days old
'needs_review': [], # No reviews
'approved': [], # Approved but not merged
'changes_requested': [], # Changes requested
'recent': [], # < 7 days old
}
for pr in prs:
if pr['age_days'] > 30:
categories['stale'].append(pr)
elif pr['has_approvals']:
categories['approved'].append(pr)
elif pr['has_changes_requested']:
categories['changes_requested'].append(pr)
elif pr['age_days'] < 7:
categories['recent'].append(pr)
else:
categories['needs_review'].append(pr)
return categories
def generate_report(repo: str, prs: list, categories: dict) -> str:
"""Generate a markdown report."""
report = f"""# PR Backlog Report — {repo}
Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
## Summary
- **Total Open PRs**: {len(prs)}
- **Stale (>30 days)**: {len(categories['stale'])}
- **Needs Review**: {len(categories['needs_review'])}
- **Approved**: {len(categories['approved'])}
- **Changes Requested**: {len(categories['changes_requested'])}
- **Recent (<7 days)**: {len(categories['recent'])}
## Recommendations
### Immediate Actions
1. **Merge approved PRs**: {len(categories['approved'])} PRs are ready to merge
2. **Review stale PRs**: {len(categories['stale'])} PRs are >30 days old
3. **Address changes requested**: {len(categories['changes_requested'])} PRs need updates
### Process Improvements
1. **Assign reviewers**: Ensure each PR has a reviewer within 24 hours
2. **Set SLAs**:
- Review within 48 hours
- Merge within 7 days of approval
- Close stale PRs after 30 days
3. **Automate**: Add CI checks to prevent backlog
## Detailed Analysis
### Stale PRs (>30 days)
"""
for pr in categories['stale']:
report += f"- **#{pr['number']}**: {pr['title']}\n"
report += f" - Age: {pr['age_days']} days\n"
report += f" - Author: {pr['user']}\n"
report += f" - URL: {pr['url']}\n\n"
report += "\n### Approved PRs (Ready to Merge)\n"
for pr in categories['approved']:
report += f"- **#{pr['number']}**: {pr['title']}\n"
report += f" - Age: {pr['age_days']} days\n"
report += f" - Author: {pr['user']}\n"
report += f" - URL: {pr['url']}\n\n"
report += "\n### Needs Review\n"
for pr in categories['needs_review']:
report += f"- **#{pr['number']}**: {pr['title']}\n"
report += f" - Age: {pr['age_days']} days\n"
report += f" - Author: {pr['user']}\n"
report += f" - URL: {pr['url']}\n\n"
return report
def main():
"""Main function."""
token = Path.home() / '.config' / 'gitea' / 'token'
if not token.exists():
print("Error: Gitea token not found")
sys.exit(1)
token_str = token.read_text().strip()
repo = "Timmy_Foundation/timmy-config"
print(f"Fetching PRs for {repo}...")
prs = get_open_prs(repo, token_str)
if not prs:
print("No open PRs found")
return
print(f"Found {len(prs)} open PRs")
# Analyze PRs
analyzed = [analyze_pr(pr) for pr in prs]
categories = categorize_prs(analyzed)
# Generate report
report = generate_report(repo, analyzed, categories)
# Save report
output_dir = Path("reports")
output_dir.mkdir(exist_ok=True)
report_file = output_dir / f"pr-backlog-{datetime.now().strftime('%Y%m%d')}.md"
report_file.write_text(report)
print(f"\nReport saved to: {report_file}")
print(f"\nSummary:")
print(f" Total PRs: {len(prs)}")
print(f" Stale: {len(categories['stale'])}")
print(f" Approved: {len(categories['approved'])}")
print(f" Needs Review: {len(categories['needs_review'])}")
if __name__ == "__main__":
main()

48
scripts/pr-safe.sh Executable file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# ═══════════════════════════════════════════════════════════════
# pr-safe.sh — Safe PR creation wrapper
#
# This script checks for existing PRs before creating a new one.
# It's a wrapper around check-existing-prs.sh that provides
# a user-friendly interface.
#
# Usage:
# ./scripts/pr-safe.sh <issue_number> [branch_name]
#
# If branch_name is not provided, it will suggest one based on
# the issue number and current timestamp.
# ═══════════════════════════════════════════════════════════════
set -euo pipefail
ISSUE_NUMBER="${1:?Usage: $0 <issue_number> [branch_name]}"
BRANCH_NAME="${2:-}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "🔍 Checking for existing PRs for issue #$ISSUE_NUMBER..."
echo ""
# Run the check
if "$SCRIPT_DIR/check-existing-prs.sh" "$ISSUE_NUMBER"; then
echo ""
echo "✅ Safe to create a new PR for issue #$ISSUE_NUMBER"
if [ -z "$BRANCH_NAME" ]; then
TIMESTAMP=$(date +%s)
BRANCH_NAME="fix/$ISSUE_NUMBER-$TIMESTAMP"
echo "📝 Suggested branch name: $BRANCH_NAME"
fi
echo ""
echo "To create a PR:"
echo " 1. Create branch: git checkout -b $BRANCH_NAME"
echo " 2. Make your changes"
echo " 3. Commit: git commit -m 'fix: Description (#$ISSUE_NUMBER)'"
echo " 4. Push: git push -u origin $BRANCH_NAME"
echo " 5. Create PR via API or web interface"
else
echo ""
echo "❌ Cannot create new PR for issue #$ISSUE_NUMBER"
echo " Existing PRs found. Review them first."
exit 1
fi