spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

claude-code / .github / workflows / claude-dedupe-issues.yml
148 lines · 5.3 KBRaw
1name: Claude Issue Dedupe
2description: Automatically dedupe GitHub issues using Claude Code
3on:
4 issues:
5 types: [opened]
6 workflow_dispatch:
7 inputs:
8 issue_number:
9 description: 'Issue number to process for duplicate detection'
10 required: true
11 type: string
12
13jobs:
14 claude-dedupe-issues:
15 runs-on: ubuntu-latest
16 timeout-minutes: 10
17 permissions:
18 contents: read
19 issues: write
20
21 steps:
22 - name: Checkout repository
23 uses: actions/checkout@v4
24
25 - name: Run Claude Code slash command
26 id: claude
27 uses: anthropics/claude-code-base-action@beta
28 with:
29 prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}"
30 anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
31 claude_args: "--model claude-sonnet-4-5-20250929"
32 # Note: GH_TOKEN only provides read access for issue viewing/searching
33 # Comment posting is handled in a separate isolated step below
34 claude_env: |
35 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36
37 # SECURITY: This step runs in isolation without access to ANTHROPIC_API_KEY
38 # It only has GITHUB_TOKEN for posting comments, preventing secret exfiltration
39 - name: Post duplicate comment (isolated from API key)
40 if: success()
41 env:
42 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43 run: |
44 ISSUE_NUMBER=${{ github.event.issue.number || inputs.issue_number }}
45 RESULT_FILE="/tmp/dedupe-result.json"
46
47 if [ ! -f "$RESULT_FILE" ]; then
48 echo "No dedupe result file found, skipping comment"
49 exit 0
50 fi
51
52 # Check if we should skip
53 if jq -e '.skip' "$RESULT_FILE" > /dev/null 2>&1; then
54 REASON=$(jq -r '.reason // "unknown"' "$RESULT_FILE")
55 echo "Skipping comment: $REASON"
56 exit 0
57 fi
58
59 # Get duplicates array
60 DUPLICATES=$(jq -r '.duplicates // []' "$RESULT_FILE")
61 COUNT=$(echo "$DUPLICATES" | jq 'length')
62
63 if [ "$COUNT" -eq 0 ]; then
64 echo "No duplicates found, skipping comment"
65 exit 0
66 fi
67
68 # Build comment body (limit to 3 duplicates for safety)
69 SAFE_COUNT=$((COUNT > 3 ? 3 : COUNT))
70
71 COMMENT="Found $SAFE_COUNT possible duplicate issue"
72 if [ "$SAFE_COUNT" -ne 1 ]; then
73 COMMENT="${COMMENT}s"
74 fi
75 COMMENT="${COMMENT}:"
76 COMMENT="${COMMENT}
77 "
78
79 for i in $(seq 0 $((SAFE_COUNT - 1))); do
80 URL=$(echo "$DUPLICATES" | jq -r ".[$i]")
81 # Validate URL format to prevent injection
82 if [[ "$URL" =~ ^https://github\.com/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/issues/[0-9]+$ ]]; then
83 COMMENT="${COMMENT}
84 $((i + 1)). $URL"
85 fi
86 done
87
88 COMMENT="${COMMENT}
89
90 This issue will be automatically closed as a duplicate in 3 days.
91
92 - If your issue is a duplicate, please close it and 👍 the existing issue instead
93 - To prevent auto-closure, add a comment or 👎 this comment
94
95 🤖 Generated with [Claude Code](https://claude.ai/code)"
96
97 # Post the comment
98 gh issue comment "$ISSUE_NUMBER" --repo "${{ github.repository }}" --body "$COMMENT"
99 echo "Posted duplicate comment on issue #$ISSUE_NUMBER"
100
101 - name: Log duplicate comment event to Statsig
102 if: always()
103 env:
104 STATSIG_API_KEY: ${{ secrets.STATSIG_API_KEY }}
105 run: |
106 ISSUE_NUMBER=${{ github.event.issue.number || inputs.issue_number }}
107 REPO=${{ github.repository }}
108
109 if [ -z "$STATSIG_API_KEY" ]; then
110 echo "STATSIG_API_KEY not found, skipping Statsig logging"
111 exit 0
112 fi
113
114 # Prepare the event payload
115 EVENT_PAYLOAD=$(jq -n \
116 --arg issue_number "$ISSUE_NUMBER" \
117 --arg repo "$REPO" \
118 --arg triggered_by "${{ github.event_name }}" \
119 '{
120 events: [{
121 eventName: "github_duplicate_comment_added",
122 value: 1,
123 metadata: {
124 repository: $repo,
125 issue_number: ($issue_number | tonumber),
126 triggered_by: $triggered_by,
127 workflow_run_id: "${{ github.run_id }}"
128 },
129 time: (now | floor | tostring)
130 }]
131 }')
132
133 # Send to Statsig API
134 echo "Logging duplicate comment event to Statsig for issue #${ISSUE_NUMBER}"
135
136 RESPONSE=$(curl -s -w "\n%{http_code}" -X POST https://events.statsigapi.net/v1/log_event \
137 -H "Content-Type: application/json" \
138 -H "STATSIG-API-KEY: ${STATSIG_API_KEY}" \
139 -d "$EVENT_PAYLOAD")
140
141 HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
142 BODY=$(echo "$RESPONSE" | head -n-1)
143
144 if [ "$HTTP_CODE" -eq 200 ] || [ "$HTTP_CODE" -eq 202 ]; then
145 echo "Successfully logged duplicate comment event for issue #${ISSUE_NUMBER}"
146 else
147 echo "Failed to log duplicate comment event for issue #${ISSUE_NUMBER}. HTTP ${HTTP_CODE}: ${BODY}"
148 fi