Skip to content

Commit 64acef4

Browse files
Copilotswissspidy
andcommitted
Reimplement issue triage using AI inference action
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
1 parent 6245837 commit 64acef4

1 file changed

Lines changed: 123 additions & 124 deletions

File tree

.github/workflows/issue-triage.yml

Lines changed: 123 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -22,121 +22,140 @@ jobs:
2222
if: github.event_name == 'issues'
2323
runs-on: ubuntu-latest
2424
steps:
25-
- name: Analyze issue and apply labels
25+
- name: Get available labels
26+
id: get-labels
2627
uses: actions/github-script@v7
2728
with:
2829
script: |
29-
// Function to determine labels based on content
30-
function determineLabels(title, body) {
31-
const content = (title + ' ' + body).toLowerCase();
32-
const labels = [];
33-
34-
// Check if it matches bug report patterns
35-
if (content.includes('bug report') ||
36-
content.includes('buggy behavior') ||
37-
content.includes('error') ||
38-
content.includes('broken') ||
39-
content.includes('not working') ||
40-
content.includes('fails to') ||
41-
content.includes('crash') ||
42-
content.includes('problem')) {
43-
labels.push('i: bug');
44-
}
30+
const labels = await github.rest.issues.listLabelsForRepo({
31+
owner: context.repo.owner,
32+
repo: context.repo.repo,
33+
per_page: 100
34+
});
35+
const labelNames = labels.data.map(label => label.name);
36+
return labelNames.join(', ');
37+
38+
- name: Set environment variables
39+
env:
40+
ISSUE_TITLE: ${{ github.event.issue.title }}
41+
ISSUE_BODY: ${{ github.event.issue.body }}
42+
LABELS_RESULT: ${{ steps.get-labels.outputs.result }}
43+
run: |
44+
{
45+
echo "AVAILABLE_LABELS=${LABELS_RESULT}"
46+
echo "ISSUE_TITLE=${ISSUE_TITLE}"
47+
echo "ISSUE_BODY<<EOF"
48+
echo "${ISSUE_BODY}"
49+
echo "EOF"
50+
} >> "$GITHUB_ENV"
51+
52+
- name: Analyze issue with AI
53+
id: ai-triage
54+
uses: actions/ai-inference@v1
55+
with:
56+
prompt: |
57+
## Role
4558
46-
// Check if it matches feature request patterns
47-
if (content.includes('feature request') ||
48-
content.includes('enhancement') ||
49-
content.includes('suggestion') ||
50-
content.includes('would like') ||
51-
content.includes('could you') ||
52-
content.includes('add support for') ||
53-
content.includes('it would be nice')) {
54-
labels.push('i: enhancement');
55-
}
59+
You are an issue triage assistant. Analyze the current GitHub
60+
issue and identify the most appropriate existing labels. Use the
61+
available tools to gather information; do not ask for information
62+
to be provided.
5663
57-
// Check if it matches support request patterns
58-
if (content.includes('support request') ||
59-
content.includes('how to') ||
60-
content.includes('question') ||
61-
content.includes('help needed') ||
62-
(content.includes('how do') &&
63-
content.includes('?'))) {
64-
labels.push('i: state:unsupported');
65-
}
64+
## Guidelines
6665
67-
return labels;
68-
}
66+
- Only use labels that are from the list of available labels.
67+
- You can choose multiple labels to apply.
68+
- When generating shell commands, you **MUST NOT** use command
69+
substitution with `$(...)`, `<(...)`, or `>(...)`. This is a
70+
security measure to prevent unintended command execution.
71+
72+
## Input Data
73+
74+
**Available Labels** (comma-separated):
75+
```
76+
${{ env.AVAILABLE_LABELS }}
77+
```
78+
79+
**Issue Title**:
80+
```
81+
${{ env.ISSUE_TITLE }}
82+
```
83+
84+
**Issue Body**:
85+
```
86+
${{ env.ISSUE_BODY }}
87+
```
88+
89+
**Output File Path**:
90+
```
91+
${{ env.GITHUB_ENV }}
92+
```
6993
70-
const issue = context.payload.issue;
71-
const issueBody = issue.body || '';
72-
const issueTitle = issue.title || '';
94+
## Steps
7395
74-
const labels = determineLabels(issueTitle, issueBody);
96+
1. Review the issue title, issue body, and available labels
97+
provided above.
98+
99+
2. Based on the issue title and issue body, classify the issue
100+
and choose all appropriate labels from the list of available
101+
labels.
102+
103+
3. Output the selected labels as a comma-separated list to the
104+
file at the path specified in "Output File Path" using the
105+
following format:
106+
```
107+
SELECTED_LABELS=label1,label2,label3
108+
```
109+
110+
- name: Apply labels
111+
uses: actions/github-script@v7
112+
with:
113+
script: |
114+
const selectedLabels = process.env.SELECTED_LABELS;
115+
if (!selectedLabels || selectedLabels.trim() === '') {
116+
console.log('No labels selected by AI');
117+
return;
118+
}
119+
120+
const labels = selectedLabels.split(',')
121+
.map(l => l.trim())
122+
.filter(l => l.length > 0);
75123
76-
// Apply labels if any were determined
77124
if (labels.length > 0) {
78125
console.log(`Applying labels: ${labels.join(', ')}`);
79126
await github.rest.issues.addLabels({
80127
owner: context.repo.owner,
81128
repo: context.repo.repo,
82-
issue_number: issue.number,
129+
issue_number: context.issue.number,
83130
labels: labels
84131
});
85132
} else {
86-
console.log(
87-
'No appropriate labels determined for this issue'
88-
);
133+
console.log('No valid labels to apply');
89134
}
90135
91136
triage-unlabeled-issues:
92137
name: Triage Unlabeled Issues
93138
if: github.event_name == 'workflow_dispatch'
94139
runs-on: ubuntu-latest
95140
steps:
96-
- name: Find and label unlabeled issues
141+
- name: Get available labels
142+
id: get-labels
97143
uses: actions/github-script@v7
98144
with:
99145
script: |
100-
// Function to determine labels based on content
101-
function determineLabels(title, body) {
102-
const content = (title + ' ' + body).toLowerCase();
103-
const labels = [];
104-
105-
// Check if it matches bug report patterns
106-
if (content.includes('bug report') ||
107-
content.includes('buggy behavior') ||
108-
content.includes('error') ||
109-
content.includes('broken') ||
110-
content.includes('not working') ||
111-
content.includes('fails to') ||
112-
content.includes('crash') ||
113-
content.includes('problem')) {
114-
labels.push('i: bug');
115-
}
116-
117-
// Check if it matches feature request patterns
118-
if (content.includes('feature request') ||
119-
content.includes('enhancement') ||
120-
content.includes('suggestion') ||
121-
content.includes('would like') ||
122-
content.includes('could you') ||
123-
content.includes('add support for') ||
124-
content.includes('it would be nice')) {
125-
labels.push('i: enhancement');
126-
}
127-
128-
// Check if it matches support request patterns
129-
if (content.includes('support request') ||
130-
content.includes('how to') ||
131-
content.includes('question') ||
132-
content.includes('help needed') ||
133-
(content.includes('how do') &&
134-
content.includes('?'))) {
135-
labels.push('i: state:unsupported');
136-
}
137-
138-
return labels;
139-
}
146+
const labels = await github.rest.issues.listLabelsForRepo({
147+
owner: context.repo.owner,
148+
repo: context.repo.repo,
149+
per_page: 100
150+
});
151+
const labelNames = labels.data.map(label => label.name);
152+
return labelNames.join(', ');
153+
154+
- name: Find and triage unlabeled issues
155+
uses: actions/github-script@v7
156+
with:
157+
script: |
158+
const availableLabels = ${{ steps.get-labels.outputs.result }};
140159
141160
// Get all open issues
142161
const issues = await github.paginate(
@@ -160,41 +179,21 @@ jobs:
160179
`Found ${unlabeledIssues.length} unlabeled issues`
161180
);
162181
163-
// Process each unlabeled issue
164-
for (const issue of unlabeledIssues) {
165-
const issueBody = issue.body || '';
166-
const issueTitle = issue.title || '';
167-
168-
const labels = determineLabels(issueTitle, issueBody);
169-
170-
// Apply labels if any were determined
171-
if (labels.length > 0) {
172-
const labelList = labels.join(', ');
173-
console.log(
174-
`Issue #${issue.number}: Applying labels: ${labelList}`
175-
);
176-
try {
177-
await github.rest.issues.addLabels({
178-
owner: context.repo.owner,
179-
repo: context.repo.repo,
180-
issue_number: issue.number,
181-
labels: labels
182-
});
183-
} catch (error) {
184-
console.error(
185-
`Failed to label issue #${issue.number}: ` +
186-
`${error.message}`
187-
);
188-
}
189-
} else {
190-
console.log(
191-
`Issue #${issue.number}: ` +
192-
`No appropriate labels determined`
193-
);
194-
}
195-
196-
// Add a small delay to avoid rate limiting
197-
await new Promise(resolve => setTimeout(resolve, 100));
182+
if (unlabeledIssues.length === 0) {
183+
console.log('No unlabeled issues to process');
184+
return;
198185
}
199186
200-
console.log('Finished processing unlabeled issues');
187+
// For manual dispatch, we'll log that AI triage is not yet
188+
// implemented for batch processing
189+
console.log(
190+
'Note: Batch AI triage for unlabeled issues requires ' +
191+
'manual triggering of individual issue workflows or ' +
192+
'implementing batch AI processing in the future.'
193+
);
194+
195+
for (const issue of unlabeledIssues) {
196+
console.log(
197+
`Issue #${issue.number}: "${issue.title}" needs triage`
198+
);
199+
}

0 commit comments

Comments
 (0)