Skip to content

Commit fc810ab

Browse files
authored
chore: triage all open issues (#1526)
1 parent 2712e70 commit fc810ab

File tree

2 files changed

+169
-121
lines changed

2 files changed

+169
-121
lines changed

.github/workflows/triage-agent.yml

Lines changed: 22 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: AI Triage - Label and Comment on New Issues
1+
name: AI Triage
22
on:
33
issues:
44
types: [opened]
@@ -8,6 +8,9 @@ on:
88
description: 'Issue number to triage (manual run). e.g. 123'
99
required: true
1010

11+
run-name: >-
12+
AI Triage for Issue #${{ github.event.issue.number || github.event.inputs.issue_number }}
13+
1114
permissions:
1215
issues: write
1316
contents: read
@@ -49,8 +52,8 @@ jobs:
4952
const labelNames = (issue.labels || []).map(label => label.name);
5053
core.setOutput('labels', JSON.stringify(labelNames));
5154
52-
- name: Start Triage Orchestration
53-
id: start_orchestration
55+
- name: Call Azure Function
56+
id: call_azure_function
5457
env:
5558
PAYLOAD: >-
5659
{
@@ -67,14 +70,13 @@ jobs:
6770
}
6871
6972
run: |
70-
# Start the durable function orchestration
71-
echo "Starting triage orchestration..."
72-
echo "Payload size: $(echo "$PAYLOAD" | wc -c) bytes"
73+
# Make the HTTP request with improved error handling and timeouts
74+
echo "Making request to triage agent..."
7375
74-
# Make the initial request to start orchestration
76+
# Add timeout handling and better error detection
7577
set +e # Don't exit on curl failure
76-
response=$(curl \
77-
--max-time 60 \
78+
response=$(timeout ${{ vars.TRIAGE_AGENT_TIMEOUT }} curl \
79+
--max-time 0 \
7880
--connect-timeout 30 \
7981
--fail-with-body \
8082
--silent \
@@ -83,16 +85,19 @@ jobs:
8385
--header "Content-Type: application/json" \
8486
--request POST \
8587
--data "$PAYLOAD" \
86-
${{ secrets.TRIAGE_START_ENDPOINT }} 2>&1)
88+
${{ secrets.TRIAGE_FUNCTION_LINK }} 2>&1)
8789
8890
curl_exit_code=$?
8991
set -e # Re-enable exit on error
9092
91-
echo "Start orchestration curl exit code: $curl_exit_code"
93+
echo "Curl exit code: $curl_exit_code"
9294
93-
# Check if curl command failed
94-
if [ $curl_exit_code -ne 0 ]; then
95-
echo "❌ Failed to start orchestration with exit code: $curl_exit_code"
95+
# Check if curl command timed out or failed
96+
if [ $curl_exit_code -eq 124 ]; then
97+
echo "❌ Request timed out after 650 seconds"
98+
exit 1
99+
elif [ $curl_exit_code -ne 0 ]; then
100+
echo "❌ Curl command failed with exit code: $curl_exit_code"
96101
echo "Response: $response"
97102
exit 1
98103
fi
@@ -110,115 +115,11 @@ jobs:
110115
exit 1
111116
fi
112117
113-
# Check if the orchestration started successfully
118+
# Check if the request was successful
114119
if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
115-
echo "✅ Triage orchestration started successfully"
116-
echo "Response: $response_body"
117-
118-
# Extract instance ID from response (assuming JSON response with instanceId field)
119-
instance_id=$(echo "$response_body" | grep -o '"instanceId":"[^"]*"' | cut -d'"' -f4)
120-
if [ -z "$instance_id" ]; then
121-
echo "❌ Failed to extract instance ID from response"
122-
echo "Response body: $response_body"
123-
exit 1
124-
fi
125-
126-
echo "Instance ID: $instance_id"
127-
echo "instance_id=$instance_id" >> $GITHUB_OUTPUT
120+
echo "✅ Azure Function call succeeded"
128121
else
129-
echo "❌ Failed to start orchestration with status code: $http_code"
122+
echo "❌ Azure Function call failed with status code: $http_code"
130123
echo "Response: $response_body"
131124
exit 1
132125
fi
133-
134-
- name: Poll Orchestration Status
135-
id: poll_status
136-
run: |
137-
# Poll the orchestration status until completion
138-
instance_id="${{ steps.start_orchestration.outputs.instance_id }}"
139-
echo "Polling status for instance: $instance_id"
140-
141-
max_attempts=120 # Maximum number of polling attempts (10 minutes with 5-second intervals)
142-
attempt=0
143-
144-
while [ $attempt -lt $max_attempts ]; do
145-
attempt=$((attempt + 1))
146-
echo "Polling attempt $attempt/$max_attempts..."
147-
148-
# Make status check request
149-
set +e # Don't exit on curl failure
150-
# Safely replace the {instanceId} placeholder using bash parameter expansion
151-
status_url="${{ secrets.TRIAGE_STATUS_ENDPOINT }}"
152-
status_url="${status_url//\{instanceId\}/$instance_id}"
153-
154-
response=$(curl \
155-
--max-time 30 \
156-
--connect-timeout 15 \
157-
--fail-with-body \
158-
--silent \
159-
--show-error \
160-
--write-out "HTTPSTATUS:%{http_code}" \
161-
--request GET \
162-
"$status_url" 2>&1)
163-
164-
curl_exit_code=$?
165-
set -e # Re-enable exit on error
166-
167-
if [ $curl_exit_code -ne 0 ]; then
168-
echo "❌ Status check failed with exit code: $curl_exit_code"
169-
echo "Response: $response"
170-
sleep 5
171-
continue
172-
fi
173-
174-
# Extract HTTP status code and response body
175-
http_code=$(echo "$response" | grep -o "HTTPSTATUS:[0-9]*" | cut -d: -f2)
176-
response_body=$(echo "$response" | sed 's/HTTPSTATUS:[0-9]*$//')
177-
178-
echo "Status check HTTP code: $http_code"
179-
180-
if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
181-
echo "Status response: $response_body"
182-
183-
# Check if orchestration is complete (using runtimeStatus field from actual response schema)
184-
runtime_status=$(echo "$response_body" | grep -o '"runtimeStatus":"[^"]*"' | cut -d'"' -f4)
185-
186-
if [ "$runtime_status" = "Completed" ] || [ "$runtime_status" = "completed" ]; then
187-
echo "✅ Triage orchestration completed successfully"
188-
# Extract and display output if available
189-
output=$(echo "$response_body" | grep -o '"output":[^,}]*' | cut -d':' -f2-)
190-
if [ "$output" != "null" ] && [ -n "$output" ]; then
191-
echo "Orchestration output: $output"
192-
fi
193-
exit 0
194-
elif [ "$runtime_status" = "Failed" ] || [ "$runtime_status" = "failed" ]; then
195-
echo "❌ Triage orchestration failed"
196-
echo "Final response: $response_body"
197-
# Extract and display output for error details if available
198-
output=$(echo "$response_body" | grep -o '"output":[^,}]*' | cut -d':' -f2-)
199-
if [ "$output" != "null" ] && [ -n "$output" ]; then
200-
echo "Error details: $output"
201-
fi
202-
exit 1
203-
elif [ "$runtime_status" = "Running" ] || [ "$runtime_status" = "running" ] || [ "$runtime_status" = "Pending" ] || [ "$runtime_status" = "pending" ]; then
204-
echo "Orchestration status: $runtime_status, waiting..."
205-
# Display last updated time for better monitoring
206-
last_updated=$(echo "$response_body" | grep -o '"lastUpdatedAt":"[^"]*"' | cut -d'"' -f4)
207-
if [ -n "$last_updated" ]; then
208-
echo "Last updated: $last_updated"
209-
fi
210-
sleep 5
211-
else
212-
echo "Unknown runtime status: $runtime_status, continuing to poll..."
213-
echo "Full response: $response_body"
214-
sleep 5
215-
fi
216-
else
217-
echo "❌ Status check returned HTTP $http_code"
218-
echo "Response: $response_body"
219-
sleep 5
220-
fi
221-
done
222-
223-
echo "❌ Orchestration polling timed out after $max_attempts attempts"
224-
exit 1
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
name: AI Triage - Process All Open Issues
2+
on:
3+
workflow_dispatch:
4+
inputs:
5+
dry_run:
6+
description: 'Dry run mode - only list issues without processing'
7+
required: false
8+
default: false
9+
type: boolean
10+
max_issues:
11+
description: 'Maximum number of issues to process (0 = all)'
12+
required: false
13+
default: '0'
14+
type: string
15+
16+
permissions:
17+
issues: write
18+
contents: read
19+
actions: write
20+
21+
jobs:
22+
get_open_issues:
23+
runs-on: ubuntu-latest
24+
outputs:
25+
issue_numbers: ${{ steps.get_issues.outputs.issue_numbers }}
26+
total_count: ${{ steps.get_issues.outputs.total_count }}
27+
28+
steps:
29+
- name: Get all open issues
30+
id: get_issues
31+
uses: actions/github-script@v6
32+
with:
33+
script: |
34+
const { data: issues } = await github.rest.issues.listForRepo({
35+
owner: context.repo.owner,
36+
repo: context.repo.repo,
37+
state: 'open',
38+
per_page: 100,
39+
sort: 'created',
40+
direction: 'asc'
41+
});
42+
43+
// Filter out pull requests (GitHub API includes PRs in issues)
44+
const actualIssues = issues.filter(issue => !issue.pull_request);
45+
46+
let issuesToProcess = actualIssues;
47+
const maxIssues = parseInt('${{ inputs.max_issues }}' || '0');
48+
49+
if (maxIssues > 0 && actualIssues.length > maxIssues) {
50+
issuesToProcess = actualIssues.slice(0, maxIssues);
51+
console.log(`Limiting to first ${maxIssues} issues out of ${actualIssues.length} total`);
52+
}
53+
54+
const issueNumbers = issuesToProcess.map(issue => issue.number);
55+
const totalCount = issuesToProcess.length;
56+
57+
console.log(`Found ${actualIssues.length} open issues, processing ${totalCount}:`);
58+
issuesToProcess.forEach(issue => {
59+
console.log(` #${issue.number}: ${issue.title}`);
60+
});
61+
62+
core.setOutput('issue_numbers', JSON.stringify(issueNumbers));
63+
core.setOutput('total_count', totalCount);
64+
65+
process_issues:
66+
runs-on: ubuntu-latest
67+
needs: get_open_issues
68+
if: needs.get_open_issues.outputs.total_count > 0
69+
70+
strategy:
71+
# Process issues one by one (max-parallel: 1)
72+
max-parallel: 1
73+
matrix:
74+
issue_number: ${{ fromJSON(needs.get_open_issues.outputs.issue_numbers) }}
75+
76+
steps:
77+
- name: Log current issue being processed
78+
run: |
79+
echo "🔄 Processing issue #${{ matrix.issue_number }}"
80+
echo "Total issues to process: ${{ needs.get_open_issues.outputs.total_count }}"
81+
82+
- name: Check if dry run mode
83+
if: inputs.dry_run == true
84+
run: |
85+
echo "🔍 DRY RUN MODE: Would process issue #${{ matrix.issue_number }}"
86+
echo "Skipping actual triage processing"
87+
88+
- name: Trigger triage workflow for issue
89+
if: inputs.dry_run != true
90+
uses: actions/github-script@v6
91+
with:
92+
script: |
93+
const issueNumber = '${{ matrix.issue_number }}';
94+
95+
try {
96+
console.log(`Triggering triage workflow for issue #${issueNumber}`);
97+
98+
const response = await github.rest.actions.createWorkflowDispatch({
99+
owner: context.repo.owner,
100+
repo: context.repo.repo,
101+
workflow_id: 'triage-agent-2.yml',
102+
ref: 'main',
103+
inputs: {
104+
issue_number: issueNumber
105+
}
106+
});
107+
108+
console.log(`✅ Successfully triggered triage workflow for issue #${issueNumber}`);
109+
110+
} catch (error) {
111+
console.error(`❌ Failed to trigger triage workflow for issue #${issueNumber}:`, error);
112+
core.setFailed(`Failed to process issue #${issueNumber}: ${error.message}`);
113+
}
114+
115+
- name: Wait for workflow completion
116+
if: inputs.dry_run != true
117+
run: |
118+
echo "⏳ Waiting for triage workflow to complete for issue #${{ matrix.issue_number }}..."
119+
echo "Timeout: ${{ vars.TRIAGE_AGENT_TIMEOUT }} seconds"
120+
sleep ${{ vars.TRIAGE_AGENT_TIMEOUT }} # Wait for triage workflow completion
121+
122+
summary:
123+
runs-on: ubuntu-latest
124+
needs: [get_open_issues, process_issues]
125+
if: always()
126+
127+
steps:
128+
- name: Print summary
129+
run: |
130+
echo "## Triage Processing Summary"
131+
echo "Total open issues found: ${{ needs.get_open_issues.outputs.total_count }}"
132+
133+
if [ "${{ inputs.dry_run }}" == "true" ]; then
134+
echo "Mode: DRY RUN (no actual processing performed)"
135+
else
136+
echo "Mode: FULL PROCESSING"
137+
fi
138+
139+
if [ "${{ needs.process_issues.result }}" == "success" ]; then
140+
echo "✅ All issues processed successfully"
141+
elif [ "${{ needs.process_issues.result }}" == "failure" ]; then
142+
echo "❌ Some issues failed to process"
143+
elif [ "${{ needs.process_issues.result }}" == "skipped" ]; then
144+
echo "⏭️ Processing was skipped (no open issues found)"
145+
else
146+
echo "⚠️ Processing completed with status: ${{ needs.process_issues.result }}"
147+
fi

0 commit comments

Comments
 (0)