Fix triage script to dynamically find and close duplicates using GitHub API

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-12-27 18:03:02 +00:00
parent 4f9f42f5c2
commit 6903901ec0
4 changed files with 338 additions and 37 deletions

View File

@@ -43,20 +43,32 @@ Now it only runs when the `deploy-production` job actually fails.
A script was created to close the duplicate issues: `scripts/triage-duplicate-issues.sh`
**To run the script:**
**The script now dynamically finds and closes duplicates:**
```bash
# Set your GitHub token (needs repo write access)
export GITHUB_TOKEN="your_github_token_here"
# Run the script
# Run the script (uses default search pattern)
./scripts/triage-duplicate-issues.sh
# Or with a custom search pattern
export SEARCH_TITLE="Your custom issue title pattern"
./scripts/triage-duplicate-issues.sh
```
The script will:
1. Add an explanatory comment to each duplicate issue
2. Close the issue with state_reason "not_planned"
3. Keep issue #124 and #24 open
**The script will:**
1. Search for all open issues matching the title pattern using GitHub API
2. Sort issues by creation date (newest first)
3. Keep the most recent issue open
4. Add an explanatory comment to each older duplicate issue
5. Close duplicate issues with state_reason "not_planned"
**Key Features:**
- ✅ Dynamic duplicate detection (no hardcoded issue numbers)
- ✅ Automatically keeps the most recent issue open
- ✅ Configurable search pattern via environment variable
- ✅ Uses GitHub API search for accurate results
## Issues Closed

View File

@@ -51,14 +51,26 @@ rollback-preparation:
### 2. Created Automation ✅
**Script:** `scripts/triage-duplicate-issues.sh`
- Bulk-closes 21 duplicate issues (#92-#122)
- Adds explanatory comment to each
- Preserves issues #124 and #24
- Dynamically finds duplicate issues using GitHub API
- Sorts by creation date and identifies most recent issue
- Bulk-closes all duplicates except the most recent one
- Adds explanatory comment to each closed issue
- Configurable via environment variables
**Features:**
- ✅ No hardcoded issue numbers - uses API search
- ✅ Automatically keeps most recent issue open
- ✅ Customizable search pattern via `SEARCH_TITLE` env var
- ✅ Comprehensive error handling and rate limiting
**Usage:**
```bash
export GITHUB_TOKEN="your_token_with_repo_write_access"
./scripts/triage-duplicate-issues.sh
# Or with custom search pattern:
export SEARCH_TITLE="Custom Issue Title"
./scripts/triage-duplicate-issues.sh
```
### 3. Created Documentation ✅

168
scripts/test-triage-logic.sh Executable file
View File

@@ -0,0 +1,168 @@
#!/bin/bash
# Comprehensive test for triage-duplicate-issues.sh logic
# This tests the core functions without making actual API calls
set -e
echo "🧪 Testing triage-duplicate-issues.sh logic"
echo "============================================="
echo ""
# Source the functions we need to test (extract them from the main script)
# For testing, we'll recreate them here
get_issues_to_close() {
local issues_data="$1"
if [ -z "$issues_data" ]; then
echo "⚠️ No duplicate issues found" >&2
return 0
fi
local total_count=$(echo "$issues_data" | wc -l)
if [ "$total_count" -le 1 ]; then
echo " Only one issue found, nothing to close" >&2
return 0
fi
# Skip the first line (most recent issue) and get the rest
echo "$issues_data" | tail -n +2 | cut -d'|' -f1
}
# Test 1: Multiple duplicate issues
echo "Test 1: Multiple duplicate issues (should close all except most recent)"
echo "-----------------------------------------------------------------------"
TEST_DATA_1='124|2025-12-27T10:30:00Z|🚨 Production Deployment Failed
122|2025-12-27T10:25:00Z|🚨 Production Deployment Failed
121|2025-12-27T10:20:00Z|🚨 Production Deployment Failed
119|2025-12-27T10:15:00Z|🚨 Production Deployment Failed
117|2025-12-27T10:10:00Z|🚨 Production Deployment Failed'
TOTAL=$(echo "$TEST_DATA_1" | wc -l)
MOST_RECENT=$(echo "$TEST_DATA_1" | head -1 | cut -d'|' -f1)
TO_CLOSE=$(get_issues_to_close "$TEST_DATA_1")
TO_CLOSE_COUNT=$(echo "$TO_CLOSE" | wc -l)
echo " Total issues found: $TOTAL"
echo " Most recent issue: #$MOST_RECENT"
echo " Issues to close: $(echo $TO_CLOSE | tr '\n' ' ')"
echo " Count to close: $TO_CLOSE_COUNT"
if [ "$MOST_RECENT" = "124" ] && [ "$TO_CLOSE_COUNT" = "4" ]; then
echo " ✅ PASS: Correctly identified most recent and 4 issues to close"
else
echo " ❌ FAIL: Expected most recent=#124, count=4"
exit 1
fi
echo ""
# Test 2: Two duplicate issues
echo "Test 2: Two duplicate issues (should close oldest, keep newest)"
echo "----------------------------------------------------------------"
TEST_DATA_2='150|2025-12-27T11:00:00Z|Bug in login
148|2025-12-27T10:55:00Z|Bug in login'
TOTAL=$(echo "$TEST_DATA_2" | wc -l)
MOST_RECENT=$(echo "$TEST_DATA_2" | head -1 | cut -d'|' -f1)
TO_CLOSE=$(get_issues_to_close "$TEST_DATA_2")
TO_CLOSE_COUNT=$(echo "$TO_CLOSE" | wc -l)
echo " Total issues found: $TOTAL"
echo " Most recent issue: #$MOST_RECENT"
echo " Issues to close: $TO_CLOSE"
echo " Count to close: $TO_CLOSE_COUNT"
if [ "$MOST_RECENT" = "150" ] && [ "$TO_CLOSE" = "148" ] && [ "$TO_CLOSE_COUNT" = "1" ]; then
echo " ✅ PASS: Correctly keeps newest (#150) and closes oldest (#148)"
else
echo " ❌ FAIL: Expected most recent=#150, to_close=#148"
exit 1
fi
echo ""
# Test 3: Single issue
echo "Test 3: Single issue (should not close anything)"
echo "-------------------------------------------------"
TEST_DATA_3='200|2025-12-27T12:00:00Z|Unique issue'
TOTAL=$(echo "$TEST_DATA_3" | wc -l)
MOST_RECENT=$(echo "$TEST_DATA_3" | head -1 | cut -d'|' -f1)
TO_CLOSE=$(get_issues_to_close "$TEST_DATA_3" 2>&1)
echo " Total issues found: $TOTAL"
echo " Most recent issue: #$MOST_RECENT"
if [ -z "$(echo "$TO_CLOSE" | grep -v "Only one issue")" ]; then
echo " ✅ PASS: Correctly identified no issues to close (only 1 issue)"
else
echo " ❌ FAIL: Should not close anything with only 1 issue"
exit 1
fi
echo ""
# Test 4: Empty input
echo "Test 4: Empty input (should handle gracefully)"
echo "----------------------------------------------"
TO_CLOSE=$(get_issues_to_close "" 2>&1)
if [ -z "$(echo "$TO_CLOSE" | grep -v "No duplicate issues")" ]; then
echo " ✅ PASS: Correctly handled empty input"
else
echo " ❌ FAIL: Should handle empty input gracefully"
exit 1
fi
echo ""
# Test 5: Date parsing and sorting verification
echo "Test 5: Verify sorting by creation date (newest first)"
echo "-------------------------------------------------------"
TEST_DATA_5='300|2025-12-27T15:00:00Z|Issue C
299|2025-12-27T14:00:00Z|Issue B
298|2025-12-27T13:00:00Z|Issue A'
MOST_RECENT=$(echo "$TEST_DATA_5" | head -1 | cut -d'|' -f1)
MOST_RECENT_DATE=$(echo "$TEST_DATA_5" | head -1 | cut -d'|' -f2)
OLDEST=$(echo "$TEST_DATA_5" | tail -1 | cut -d'|' -f1)
echo " Most recent: #$MOST_RECENT at $MOST_RECENT_DATE"
echo " Oldest: #$OLDEST"
if [ "$MOST_RECENT" = "300" ] && [ "$OLDEST" = "298" ]; then
echo " ✅ PASS: Correctly sorted by date (newest first)"
else
echo " ❌ FAIL: Sorting is incorrect"
exit 1
fi
echo ""
# Test 6: jq parsing simulation (test data format)
echo "Test 6: Verify data format compatibility with jq"
echo "-------------------------------------------------"
MOCK_JSON='{"items": [
{"number": 124, "created_at": "2025-12-27T10:30:00Z", "title": "Test"},
{"number": 122, "created_at": "2025-12-27T10:25:00Z", "title": "Test"}
]}'
# Test that jq can parse and format the data correctly
PARSED=$(echo "$MOCK_JSON" | jq -r '.items | sort_by(.created_at) | reverse | .[] | "\(.number)|\(.created_at)|\(.title)"')
FIRST_ISSUE=$(echo "$PARSED" | head -1 | cut -d'|' -f1)
if [ "$FIRST_ISSUE" = "124" ]; then
echo " ✅ PASS: jq parsing and formatting works correctly"
else
echo " ❌ FAIL: jq parsing failed"
exit 1
fi
echo ""
echo "============================================="
echo "✅ All tests passed!"
echo ""
echo "Summary:"
echo " - Correctly identifies most recent issue"
echo " - Closes all duplicates except the most recent"
echo " - Handles edge cases (single issue, empty input)"
echo " - Date sorting works correctly"
echo " - Data format compatible with GitHub API response"

View File

@@ -1,53 +1,164 @@
#!/bin/bash
# Script to bulk-close duplicate "Production Deployment Failed" issues
# These were created by a misconfigured workflow that triggered rollback issues
# on pre-deployment validation failures rather than actual deployment failures.
# Script to bulk-close duplicate issues found via GitHub API
# Dynamically finds issues with duplicate titles and closes all except the most recent one
#
# Usage:
# export GITHUB_TOKEN="ghp_your_token_here"
# ./triage-duplicate-issues.sh
#
# Or with custom search pattern:
# export GITHUB_TOKEN="ghp_your_token_here"
# export SEARCH_TITLE="Custom Issue Title"
# ./triage-duplicate-issues.sh
#
# The script will:
# 1. Search for all open issues matching the SEARCH_TITLE pattern
# 2. Sort them by creation date (newest first)
# 3. Keep the most recent issue open
# 4. Close all other duplicates with an explanatory comment
set -e
GITHUB_TOKEN="${GITHUB_TOKEN}"
usage() {
echo "Usage: $0"
echo ""
echo "Environment variables:"
echo " GITHUB_TOKEN (required) GitHub personal access token with repo access"
echo " SEARCH_TITLE (optional) Issue title pattern to search for"
echo " Default: '🚨 Production Deployment Failed - Rollback Required'"
echo ""
echo "Example:"
echo " export GITHUB_TOKEN='ghp_xxxxxxxxxxxx'"
echo " export SEARCH_TITLE='Duplicate bug report'"
echo " $0"
exit 1
}
# Check for help flag
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
usage
fi
if [ -z "$GITHUB_TOKEN" ]; then
echo "❌ GITHUB_TOKEN environment variable is required"
exit 1
echo ""
usage
fi
OWNER="johndoe6345789"
REPO="metabuilder"
# Issues to close - all the duplicate deployment failure issues except the most recent (#124)
ISSUES_TO_CLOSE=(92 93 95 96 97 98 99 100 101 102 104 105 107 108 111 113 115 117 119 121 122)
# Search pattern for duplicate issues (can be customized)
SEARCH_TITLE="${SEARCH_TITLE:-🚨 Production Deployment Failed - Rollback Required}"
# Function to fetch issues by title pattern
fetch_duplicate_issues() {
local search_query="$1"
echo "🔍 Searching for issues with title: \"$search_query\"" >&2
# Use GitHub API to search for issues by title
# Filter by: is:issue, is:open, repo, and title match
local encoded_query
encoded_query=$(echo "is:issue is:open repo:$OWNER/$REPO in:title $search_query" | jq -sRr @uri)
local response
response=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/search/issues?q=$encoded_query&sort=created&order=desc&per_page=100")
# Check for API errors
if echo "$response" | jq -e '.message' > /dev/null 2>&1; then
local error_msg
error_msg=$(echo "$response" | jq -r '.message')
echo "❌ GitHub API error: $error_msg" >&2
return 1
fi
# Extract issue numbers and creation dates, sorted by creation date (newest first)
echo "$response" | jq -r '.items | sort_by(.created_at) | reverse | .[] | "\(.number)|\(.created_at)|\(.title)"'
}
# Function to determine which issues to close (all except the most recent)
get_issues_to_close() {
local issues_data="$1"
if [ -z "$issues_data" ]; then
echo "⚠️ No duplicate issues found" >&2
return 0
fi
local total_count
total_count=$(echo "$issues_data" | wc -l)
if [ "$total_count" -le 1 ]; then
echo " Only one issue found, nothing to close" >&2
return 0
fi
# Skip the first line (most recent issue) and get the rest
echo "$issues_data" | tail -n +2 | cut -d'|' -f1
}
# Fetch all duplicate issues
ISSUES_DATA=$(fetch_duplicate_issues "$SEARCH_TITLE")
if [ -z "$ISSUES_DATA" ]; then
echo "✨ No duplicate issues found. Nothing to do!"
exit 0
fi
# Parse the data
TOTAL_ISSUES=$(echo "$ISSUES_DATA" | wc -l)
MOST_RECENT=$(echo "$ISSUES_DATA" | head -1 | cut -d'|' -f1)
MOST_RECENT_DATE=$(echo "$ISSUES_DATA" | head -1 | cut -d'|' -f2)
echo "📊 Found $TOTAL_ISSUES duplicate issues"
echo "📌 Most recent issue: #$MOST_RECENT (created: $MOST_RECENT_DATE)"
echo ""
# Get list of issues to close
ISSUES_TO_CLOSE_DATA=$(get_issues_to_close "$ISSUES_DATA")
if [ -z "$ISSUES_TO_CLOSE_DATA" ]; then
echo "✨ No issues need to be closed!"
exit 0
fi
# Convert to array
ISSUES_TO_CLOSE=()
while IFS= read -r issue_num; do
ISSUES_TO_CLOSE+=("$issue_num")
done <<< "$ISSUES_TO_CLOSE_DATA"
CLOSE_COMMENT='🤖 **Automated Triage: Closing Duplicate Issue**
This issue was automatically created by a misconfigured workflow. The deployment workflow was creating "rollback required" issues when **pre-deployment validation** failed, not when actual deployments failed.
**Root Cause:**
- The `rollback-preparation` job had `if: failure()` which triggered when ANY upstream job failed
- It should have been `if: needs.deploy-production.result == '"'"'failure'"'"'` to only trigger on actual deployment failures
This issue has been identified as a duplicate. Multiple issues with the same title were found, and this script automatically closes all duplicates except the most recent one.
**Resolution:**
- ✅ Fixed the workflow in the latest commit
- ✅ Keeping issue #124 as the canonical tracking issue
- ✅ Closing this and other duplicate issues created by the same root cause
- ✅ Keeping the most recent issue (#'"$MOST_RECENT"') as the canonical tracking issue
- ✅ Closing this and other duplicate issues to maintain a clean issue tracker
**No Action Required** - These were false positives and no actual production deployments failed.
**How duplicates were identified:**
- Search pattern: "'"$SEARCH_TITLE"'"
- Total duplicates found: '"$TOTAL_ISSUES"'
- Keeping most recent: Issue #'"$MOST_RECENT"' (created '"$MOST_RECENT_DATE"')
**No Action Required** - Please refer to issue #'"$MOST_RECENT"' for continued discussion.
---
*For questions about this automated triage, see the commit that fixed the workflow.*'
*This closure was performed by an automated triage script. For questions, see `scripts/triage-duplicate-issues.sh`*'
close_issue() {
local issue_number=$1
# Add comment explaining closure
echo "📝 Adding comment to issue #${issue_number}..."
curl -s -X POST \
if curl -s -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/$OWNER/$REPO/issues/$issue_number/comments" \
-d "{\"body\": $(echo "$CLOSE_COMMENT" | jq -Rs .)}" > /dev/null
if [ $? -eq 0 ]; then
-d "{\"body\": $(echo "$CLOSE_COMMENT" | jq -Rs .)}" > /dev/null; then
echo "✅ Added comment to issue #${issue_number}"
else
echo "❌ Failed to add comment to issue #${issue_number}"
@@ -56,13 +167,11 @@ close_issue() {
# Close the issue
echo "🔒 Closing issue #${issue_number}..."
curl -s -X PATCH \
if curl -s -X PATCH \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/$OWNER/$REPO/issues/$issue_number" \
-d '{"state": "closed", "state_reason": "not_planned"}' > /dev/null
if [ $? -eq 0 ]; then
-d '{"state": "closed", "state_reason": "not_planned"}' > /dev/null; then
echo "✅ Closed issue #${issue_number}"
else
echo "❌ Failed to close issue #${issue_number}"
@@ -76,6 +185,7 @@ main() {
echo "🔧 Starting bulk issue triage..."
echo ""
echo "📋 Planning to close ${#ISSUES_TO_CLOSE[@]} duplicate issues"
echo "📌 Keeping issue #$MOST_RECENT open (most recent)"
echo ""
for issue_number in "${ISSUES_TO_CLOSE[@]}"; do
@@ -86,9 +196,8 @@ main() {
echo "✨ Triage complete!"
echo ""
echo "📌 Keeping open:"
echo " - Issue #124 (most recent deployment failure - canonical tracking issue)"
echo " - Issue #24 (Renovate Dependency Dashboard - legitimate)"
echo "📌 Kept open: Issue #$MOST_RECENT (most recent, created $MOST_RECENT_DATE)"
echo "🔒 Closed: ${#ISSUES_TO_CLOSE[@]} duplicate issues"
}
main