Skip to content

Commit aa33bb4

Browse files
authored
Merge pull request #167 from wp-cli/copilot/fix-1570774-181716041-20411eca-34f5-409a-a217-dd2c5535347a
2 parents 4e30390 + 2336b80 commit aa33bb4

File tree

3 files changed

+348
-0
lines changed

3 files changed

+348
-0
lines changed

.github/workflows/issue-triage.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
name: Issue Triage
3+
4+
'on':
5+
issues:
6+
types: [opened]
7+
workflow_dispatch:
8+
inputs:
9+
issue_number:
10+
description: 'Issue number to triage (leave empty to process all)'
11+
required: false
12+
type: string
13+
14+
jobs:
15+
issue-triage:
16+
uses: wp-cli/.github/.github/workflows/reusable-issue-triage.yml@main
17+
with:
18+
issue_number: ${{ github.event_name == 'workflow_dispatch' && inputs.issue_number || github.event.issue.number }}
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
---
2+
name: Issue Triage
3+
4+
'on':
5+
workflow_call:
6+
inputs:
7+
issue_number:
8+
description: 'Issue number to triage (leave empty to process all)'
9+
required: false
10+
type: string
11+
12+
permissions:
13+
issues: write
14+
contents: read
15+
16+
jobs:
17+
triage-new-issue:
18+
name: Triage New Issue
19+
if: github.event_name == 'issues'
20+
runs-on: ubuntu-latest
21+
steps:
22+
- name: Get available labels
23+
id: get-labels
24+
uses: actions/github-script@v7
25+
with:
26+
script: |
27+
const labels = await github.rest.issues.listLabelsForRepo({
28+
owner: context.repo.owner,
29+
repo: context.repo.repo,
30+
per_page: 100
31+
});
32+
const labelNames = labels.data.map(label => label.name);
33+
return labelNames.join(', ');
34+
35+
- name: Set environment variables
36+
env:
37+
ISSUE_TITLE: ${{ github.event.issue.title }}
38+
ISSUE_BODY: ${{ github.event.issue.body }}
39+
LABELS_RESULT: ${{ steps.get-labels.outputs.result }}
40+
run: |
41+
{
42+
echo "AVAILABLE_LABELS=${LABELS_RESULT}"
43+
echo "ISSUE_TITLE=${ISSUE_TITLE}"
44+
echo "ISSUE_BODY<<EOF"
45+
echo "${ISSUE_BODY}"
46+
echo "EOF"
47+
} >> "$GITHUB_ENV"
48+
49+
- name: Analyze issue with AI
50+
id: ai-triage
51+
uses: actions/ai-inference@v1
52+
with:
53+
prompt: |
54+
## Role
55+
56+
You are an issue triage assistant. Analyze the current GitHub
57+
issue and identify the most appropriate existing labels. Use the
58+
available tools to gather information; do not ask for information
59+
to be provided.
60+
61+
## Guidelines
62+
63+
- Only use labels that are from the list of available labels.
64+
- You can choose multiple labels to apply.
65+
- When generating shell commands, you **MUST NOT** use command
66+
substitution with `$(...)`, `<(...)`, or `>(...)`. This is a
67+
security measure to prevent unintended command execution.
68+
69+
## Input Data
70+
71+
**Available Labels** (comma-separated):
72+
```
73+
${{ env.AVAILABLE_LABELS }}
74+
```
75+
76+
**Issue Title**:
77+
```
78+
${{ env.ISSUE_TITLE }}
79+
```
80+
81+
**Issue Body**:
82+
```
83+
${{ env.ISSUE_BODY }}
84+
```
85+
86+
## Steps
87+
88+
1. Review the issue title, issue body, and available labels
89+
provided above.
90+
91+
2. Based on the issue title and issue body, classify the issue
92+
and choose all appropriate labels from the list of available
93+
labels.
94+
95+
3. Return only the selected labels as a comma-separated list,
96+
with no additional text or explanation. For example:
97+
```
98+
label1, label2, label3
99+
```
100+
101+
- name: Apply labels
102+
if: steps.ai-triage.outputs.response != ''
103+
uses: actions/github-script@v7
104+
env:
105+
AI_RESPONSE: ${{ steps.ai-triage.outputs.response }}
106+
with:
107+
script: |
108+
const response = process.env.AI_RESPONSE;
109+
if (!response || response.trim() === '') {
110+
console.log('No labels selected by AI');
111+
return;
112+
}
113+
114+
const labels = response.split(',')
115+
.map(l => l.trim())
116+
.filter(l => l.length > 0);
117+
118+
if (labels.length > 0) {
119+
console.log(`Applying labels: ${labels.join(', ')}`);
120+
await github.rest.issues.addLabels({
121+
owner: context.repo.owner,
122+
repo: context.repo.repo,
123+
issue_number: context.issue.number,
124+
labels: labels
125+
});
126+
} else {
127+
console.log('No valid labels to apply');
128+
}
129+
130+
triage-unlabeled-issues:
131+
name: Triage Unlabeled Issues
132+
if: |
133+
github.event_name == 'workflow_dispatch' &&
134+
inputs.issue_number == ''
135+
runs-on: ubuntu-latest
136+
steps:
137+
- name: Find and dispatch triage for unlabeled issues
138+
uses: actions/github-script@v7
139+
with:
140+
script: |
141+
// Get all open issues
142+
const issues = await github.paginate(
143+
github.rest.issues.listForRepo,
144+
{
145+
owner: context.repo.owner,
146+
repo: context.repo.repo,
147+
state: 'open',
148+
per_page: 100
149+
}
150+
);
151+
152+
console.log(`Found ${issues.length} open issues`);
153+
154+
// Filter issues without labels
155+
const unlabeledIssues = issues.filter(issue =>
156+
!issue.pull_request && issue.labels.length === 0
157+
);
158+
159+
console.log(
160+
`Found ${unlabeledIssues.length} unlabeled issues`
161+
);
162+
163+
if (unlabeledIssues.length === 0) {
164+
console.log('No unlabeled issues to process');
165+
return;
166+
}
167+
168+
// Dispatch triage workflow for each unlabeled issue
169+
for (const issue of unlabeledIssues) {
170+
console.log(
171+
`Dispatching triage for issue #${issue.number}: ` +
172+
`"${issue.title}"`
173+
);
174+
175+
try {
176+
await github.rest.actions.createWorkflowDispatch({
177+
owner: context.repo.owner,
178+
repo: context.repo.repo,
179+
workflow_id: 'issue-triage.yml',
180+
ref: context.ref || 'main',
181+
inputs: {
182+
issue_number: issue.number.toString()
183+
}
184+
});
185+
} catch (error) {
186+
console.error(
187+
`Failed to dispatch triage for issue #${issue.number}: ` +
188+
`${error.message}`
189+
);
190+
}
191+
192+
// Add a small delay to avoid rate limiting
193+
await new Promise(resolve => setTimeout(resolve, 100));
194+
}
195+
196+
console.log('Finished dispatching triage workflows');
197+
198+
triage-single-issue:
199+
name: Triage Single Issue
200+
if: |
201+
github.event_name == 'workflow_dispatch' &&
202+
inputs.issue_number != ''
203+
runs-on: ubuntu-latest
204+
steps:
205+
- name: Get available labels
206+
id: get-labels
207+
uses: actions/github-script@v7
208+
with:
209+
script: |
210+
const labels = await github.rest.issues.listLabelsForRepo({
211+
owner: context.repo.owner,
212+
repo: context.repo.repo,
213+
per_page: 100
214+
});
215+
const labelNames = labels.data.map(label => label.name);
216+
return labelNames.join(', ');
217+
218+
- name: Get issue details
219+
id: get-issue
220+
uses: actions/github-script@v7
221+
with:
222+
script: |
223+
const issue = await github.rest.issues.get({
224+
owner: context.repo.owner,
225+
repo: context.repo.repo,
226+
issue_number: parseInt('${{ inputs.issue_number }}')
227+
});
228+
return {
229+
title: issue.data.title,
230+
body: issue.data.body || ''
231+
};
232+
233+
- name: Set environment variables
234+
env:
235+
ISSUE_TITLE: ${{ fromJSON(steps.get-issue.outputs.result).title }}
236+
ISSUE_BODY: ${{ fromJSON(steps.get-issue.outputs.result).body }}
237+
LABELS_RESULT: ${{ steps.get-labels.outputs.result }}
238+
run: |
239+
{
240+
echo "AVAILABLE_LABELS=${LABELS_RESULT}"
241+
echo "ISSUE_TITLE=${ISSUE_TITLE}"
242+
echo "ISSUE_BODY<<EOF"
243+
echo "${ISSUE_BODY}"
244+
echo "EOF"
245+
} >> "$GITHUB_ENV"
246+
247+
- name: Analyze issue with AI
248+
id: ai-triage
249+
uses: actions/ai-inference@v1
250+
with:
251+
prompt: |
252+
## Role
253+
254+
You are an issue triage assistant. Analyze the current GitHub
255+
issue and identify the most appropriate existing labels. Use the
256+
available tools to gather information; do not ask for information
257+
to be provided.
258+
259+
## Guidelines
260+
261+
- Only use labels that are from the list of available labels.
262+
- You can choose multiple labels to apply.
263+
- When generating shell commands, you **MUST NOT** use command
264+
substitution with `$(...)`, `<(...)`, or `>(...)`. This is a
265+
security measure to prevent unintended command execution.
266+
267+
## Input Data
268+
269+
**Available Labels** (comma-separated):
270+
```
271+
${{ env.AVAILABLE_LABELS }}
272+
```
273+
274+
**Issue Title**:
275+
```
276+
${{ env.ISSUE_TITLE }}
277+
```
278+
279+
**Issue Body**:
280+
```
281+
${{ env.ISSUE_BODY }}
282+
```
283+
284+
## Steps
285+
286+
1. Review the issue title, issue body, and available labels
287+
provided above.
288+
289+
2. Based on the issue title and issue body, classify the issue
290+
and choose all appropriate labels from the list of available
291+
labels.
292+
293+
3. Return only the selected labels as a comma-separated list,
294+
with no additional text or explanation. For example:
295+
```
296+
label1, label2, label3
297+
```
298+
299+
- name: Apply labels
300+
if: steps.ai-triage.outputs.response != ''
301+
uses: actions/github-script@v7
302+
env:
303+
AI_RESPONSE: ${{ steps.ai-triage.outputs.response }}
304+
ISSUE_NUMBER: ${{ inputs.issue_number }}
305+
with:
306+
script: |
307+
const response = process.env.AI_RESPONSE;
308+
const issueNumber = parseInt(process.env.ISSUE_NUMBER);
309+
310+
if (!response || response.trim() === '') {
311+
console.log('No labels selected by AI');
312+
return;
313+
}
314+
315+
const labels = response.split(',')
316+
.map(l => l.trim())
317+
.filter(l => l.length > 0);
318+
319+
if (labels.length > 0) {
320+
console.log(`Applying labels: ${labels.join(', ')}`);
321+
await github.rest.issues.addLabels({
322+
owner: context.repo.owner,
323+
repo: context.repo.repo,
324+
issue_number: issueNumber,
325+
labels: labels
326+
});
327+
} else {
328+
console.log('No valid labels to apply');
329+
}

.github/workflows/sync-workflows.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ jobs:
2424
^.editorconfig
2525
^.github/workflows/copilot-setup-steps.yml
2626
^.github/workflows/regenerate-readme.yml
27+
^.github/workflows/issue-triage.yml
2728
^.github/workflows/check-branch-alias.yml
2829
^.github/workflows/manage-labels.yml
2930
^AGENTS.md

0 commit comments

Comments
 (0)