Skip to content

Commit 012008a

Browse files
committed
build: automatically triage issues using Gemini API
1 parent 5fc395c commit 012008a

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

.github/scripts/triage_issue.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import os
2+
import json
3+
import urllib.request
4+
import sys
5+
6+
def get_gemini_response(api_key, prompt):
7+
url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key={api_key}"
8+
headers = {'Content-Type': 'application/json'}
9+
data = {
10+
"contents": [{
11+
"parts": [{"text": prompt}]
12+
}],
13+
"generationConfig": {
14+
"response_mime_type": "application/json"
15+
}
16+
}
17+
18+
req = urllib.request.Request(url, data=json.dumps(data).encode('utf-8'), headers=headers)
19+
try:
20+
with urllib.request.urlopen(req) as response:
21+
res_data = json.loads(response.read().decode('utf-8'))
22+
return res_data['candidates'][0]['content']['parts'][0]['text']
23+
except Exception as e:
24+
print(f"Error calling Gemini API: {e}", file=sys.stderr)
25+
return None
26+
27+
def main():
28+
api_key = os.getenv("GEMINI_API_KEY")
29+
issue_title = os.getenv("ISSUE_TITLE")
30+
issue_body = os.getenv("ISSUE_BODY")
31+
32+
if not api_key:
33+
print("GEMINI_API_KEY not found", file=sys.stderr)
34+
sys.exit(1)
35+
36+
prompt = f"""
37+
You are an expert software engineer and triage assistant.
38+
Analyze the following GitHub Issue details and suggest appropriate labels.
39+
40+
Issue Title: {issue_title}
41+
Issue Description: {issue_body}
42+
43+
Triage Criteria:
44+
- Severity:
45+
- priority: p0: Critical issues, crashes, security vulnerabilities (specifically if it mentions "crash" or "exception").
46+
- priority: p1: Important issues that block release.
47+
- priority: p2: Normal priority bugs or improvements.
48+
- priority: p3: Minor enhancements or non-critical fixes.
49+
- priority: p4: Low priority, nice-to-have eventually.
50+
- Type:
51+
- type: docs: Documentation issues or requests.
52+
- type: typo: Mentioning typos in the codebase or UI.
53+
- type: test: Issues related to testing.
54+
- type: feature: Feature requests.
55+
- type: bug: Bug reports.
56+
- Environment:
57+
- environment: no-google-play: If the issue specifically mentions devices without Google Play services, Huawei devices, or microG.
58+
59+
Return a JSON object with a 'labels' key containing an array of suggested label names.
60+
The response MUST be valid JSON.
61+
Example: {{"labels": ["priority: p2", "type: bug"]}}
62+
"""
63+
64+
response_text = get_gemini_response(api_key, prompt)
65+
if response_text:
66+
try:
67+
# Clean up response text in case it has markdown wrapping
68+
if response_text.startswith("```json"):
69+
response_text = response_text.replace("```json", "", 1).replace("```", "", 1).strip()
70+
71+
result = json.loads(response_text)
72+
labels = result.get("labels", [])
73+
# Print labels as a comma-separated string for GitHub Actions
74+
print(",".join(labels))
75+
except Exception as e:
76+
print(f"Error parsing Gemini response: {e}", file=sys.stderr)
77+
print(f"Raw response: {response_text}", file=sys.stderr)
78+
sys.exit(1)
79+
else:
80+
sys.exit(1)
81+
82+
if __name__ == "__main__":
83+
main()

.github/workflows/triage-issue.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Issue Triage with Gemini
2+
3+
on:
4+
issues:
5+
types: [opened, edited]
6+
pull_request:
7+
workflow_dispatch:
8+
inputs:
9+
title:
10+
description: 'Mock Issue Title'
11+
default: 'Test Issue'
12+
body:
13+
description: 'Mock Issue Body'
14+
default: 'This is a test issue description.'
15+
16+
jobs:
17+
triage:
18+
runs-on: ubuntu-latest
19+
permissions:
20+
issues: write
21+
contents: read
22+
steps:
23+
- name: Checkout code
24+
uses: actions/checkout@v4
25+
26+
- name: Set up Python
27+
uses: actions/setup-python@v5
28+
with:
29+
python-version: '3.x'
30+
31+
- name: Run Triage Script
32+
id: run_script
33+
env:
34+
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
35+
ISSUE_TITLE: ${{ github.event.issue.title || github.event.inputs.title }}
36+
ISSUE_BODY: ${{ github.event.issue.body || github.event.inputs.body }}
37+
run: |
38+
labels=$(python .github/scripts/triage_issue.py)
39+
echo "labels=$labels" >> $GITHUB_OUTPUT
40+
41+
- name: Apply Labels
42+
if: steps.run_script.outputs.labels != ''
43+
env:
44+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45+
run: |
46+
# Convert comma-separated labels to gh command arguments
47+
IFS=',' read -ra ADDR <<< "${{ steps.run_script.outputs.labels }}"
48+
for i in "${ADDR[@]}"; do
49+
# Trim whitespace and only add if not empty
50+
label=$(echo "$i" | xargs)
51+
if [ -n "$label" ]; then
52+
gh issue edit ${{ github.event.issue.number }} --add-label "$label"
53+
fi
54+
done

0 commit comments

Comments
 (0)