Skip to content

Commit 0c4a4d2

Browse files
committed
feat: improve gemini issue triage workflow
This commit improves the gemini issue triage workflow by: - Passing the available labels as an environment variable to the Gemini CLI. - Using the output of the Gemini CLI to apply labels to the issue. - Adding a step to get all repository labels and pass them to the gemini-cli. - Updating the prompt to classify the issue and output the labels in JSON format. - Adding a step to apply the labels to the issue using the github-script.
1 parent 51fcbb2 commit 0c4a4d2

File tree

4 files changed

+392
-92
lines changed

4 files changed

+392
-92
lines changed

.github/workflows/gemini-issue-automated-triage.yml

Lines changed: 87 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,30 @@ jobs:
5555
app-id: '${{ vars.APP_ID }}'
5656
private-key: '${{ secrets.APP_PRIVATE_KEY }}'
5757

58-
- name: 'Run Gemini Issue Triage'
58+
- name: 'Get Repository Labels'
59+
id: 'get_labels'
60+
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
61+
with:
62+
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
63+
script: |-
64+
const { data: labels } = await github.rest.issues.listLabelsForRepo({
65+
owner: context.repo.owner,
66+
repo: context.repo.repo,
67+
});
68+
const labelNames = labels.map(label => label.name);
69+
core.setOutput('available_labels', labelNames.join(','));
70+
console.log(`Found ${labelNames.length} labels:`, labelNames);
71+
return labelNames;
72+
73+
- name: 'Run Gemini Issue Analysis'
5974
uses: './'
60-
id: 'gemini_issue_triage'
75+
id: 'gemini_issue_analysis'
6176
env:
62-
GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
6377
ISSUE_TITLE: '${{ github.event.issue.title }}'
6478
ISSUE_BODY: '${{ github.event.issue.body }}'
6579
ISSUE_NUMBER: '${{ github.event.issue.number }}'
6680
REPOSITORY: '${{ github.repository }}'
81+
AVAILABLE_LABELS: '${{ steps.get_labels.outputs.available_labels }}'
6782
with:
6883
gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}'
6984
gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}'
@@ -77,9 +92,7 @@ jobs:
7792
{
7893
"maxSessionTurns": 25,
7994
"coreTools": [
80-
"run_shell_command(echo)",
81-
"run_shell_command(gh label list)",
82-
"run_shell_command(gh issue edit)"
95+
"run_shell_command(echo)"
8396
],
8497
"telemetry": {
8598
"enabled": true,
@@ -90,41 +103,92 @@ jobs:
90103
## Role
91104
92105
You are an issue triage assistant. Analyze the current GitHub issue
93-
and apply the most appropriate existing labels. Use the available
106+
and identify the most appropriate existing labels. Use the available
94107
tools to gather information; do not ask for information to be
95108
provided.
96109
97110
## Steps
98111
99-
1. Run: `gh label list` to get all available labels.
112+
1. Review the available labels in the environment variable: "${AVAILABLE_LABELS}".
100113
2. Review the issue title and body provided in the environment
101114
variables: "${ISSUE_TITLE}" and "${ISSUE_BODY}".
102-
3. Classify issues by their kind (bug, enhancement, documentation,
103-
cleanup, etc) and their priority (p0, p1, p2, p3). Set the
104-
labels accoridng to the format `kind/*` and `priority/*` patterns.
105-
4. Apply the selected labels to this issue using:
106-
`gh issue edit "${ISSUE_NUMBER}" --add-label "label1,label2"`
107-
5. If the "status/needs-triage" label is present, remove it using:
108-
`gh issue edit "${ISSUE_NUMBER}" --remove-label "status/needs-triage"`
115+
3. Classify issue by the appropriate labels from the available labels.
116+
4. Output the appropriate labels for this issue in JSON format, for example:
117+
```
118+
{"labels_to_add": ["kind/bug", "priority/p2"], "labels_to_remove": ["status/needs-triage"]}
119+
```
120+
5. If the issue cannot be classified using the available labels, output:
121+
```
122+
{"labels_to_add": [], "labels_to_remove": []}
123+
```
109124
110125
## Guidelines
111126
112127
- Only use labels that already exist in the repository
113-
- Do not add comments or modify the issue content
114-
- Triage only the current issue
115128
- Assign all applicable labels based on the issue content
116129
- Reference all shell variables as "${VAR}" (with quotes and braces)
130+
- Output only valid JSON format
131+
- Do not include any explanation or additional text, just the JSON
117132
118-
- name: 'Post Issue Triage Failure Comment'
133+
- name: 'Apply Labels to Issue'
119134
if: |-
120-
${{ failure() && steps.gemini_issue_triage.outcome == 'failure' }}
135+
${{ steps.gemini_issue_analysis.outputs.summary != '' }}
136+
env:
137+
REPOSITORY: '${{ github.repository }}'
138+
ISSUE_NUMBER: '${{ github.event.issue.number }}'
139+
LABELS_OUTPUT: '${{ steps.gemini_issue_analysis.outputs.summary }}'
140+
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
141+
with:
142+
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
143+
script: |-
144+
// Strip code block markers if present
145+
let labelsJson = process.env.LABELS_OUTPUT;
146+
labelsJson = labelsJson.replace(/^```(?:json)?\s*/, '').replace(/\s*```$/, '').trim();
147+
const triageResult = JSON.parse(labelsJson);
148+
console.log('Triage result:', triageResult);
149+
150+
// Apply labels
151+
if (triageResult.labels_to_add && triageResult.labels_to_add.length > 0) {
152+
await github.rest.issues.addLabels({
153+
owner: context.repo.owner,
154+
repo: context.repo.repo,
155+
issue_number: parseInt(process.env.ISSUE_NUMBER),
156+
labels: triageResult.labels_to_add
157+
});
158+
console.log(`Applied labels: ${triageResult.labels_to_add.join(', ')}`);
159+
}
160+
161+
// Remove labels
162+
if (triageResult.labels_to_remove && triageResult.labels_to_remove.length > 0) {
163+
for (const label of triageResult.labels_to_remove) {
164+
try {
165+
await github.rest.issues.removeLabel({
166+
owner: context.repo.owner,
167+
repo: context.repo.repo,
168+
issue_number: parseInt(process.env.ISSUE_NUMBER),
169+
name: label
170+
});
171+
console.log(`Removed label: ${label}`);
172+
} catch (error) {
173+
// Label might not exist on the issue, which is fine
174+
console.log(`Could not remove label ${label}: ${error.message}`);
175+
}
176+
}
177+
}
178+
179+
- name: 'Post Issue Analysis Failure Comment'
180+
if: |-
181+
${{ failure() && steps.gemini_issue_analysis.outcome == 'failure' }}
182+
env:
183+
ISSUE_NUMBER: '${{ github.event.issue.number }}'
184+
RUN_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
121185
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
122186
with:
123187
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
124188
script: |-
125189
github.rest.issues.createComment({
126-
owner: '${{ github.repository }}'.split('/')[0],
127-
repo: '${{ github.repository }}'.split('/')[1],
128-
issue_number: '${{ github.event.issue.number }}',
129-
body: 'There is a problem with the Gemini CLI issue triaging. Please check the [action logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.'
190+
owner: context.repo.owner,
191+
repo: context.repo.repo,
192+
issue_number: parseInt(process.env.ISSUE_NUMBER),
193+
body: 'There is a problem with the Gemini CLI issue triaging. Please check the [action logs](${process.env.RUN_URL}) for details.'
130194
})

.github/workflows/gemini-issue-scheduled-triage.yml

Lines changed: 109 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,30 @@ jobs:
6363
ISSUE_COUNT="$(echo "${ISSUES}" | jq 'length')"
6464
echo "✅ Found ${ISSUE_COUNT} issues to triage! 🎯"
6565
66-
- name: 'Run Gemini Issue Triage'
66+
- name: 'Get Repository Labels'
67+
id: 'get_labels'
68+
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
69+
with:
70+
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
71+
script: |-
72+
const { data: labels } = await github.rest.issues.listLabelsForRepo({
73+
owner: context.repo.owner,
74+
repo: context.repo.repo,
75+
});
76+
const labelNames = labels.map(label => label.name);
77+
core.setOutput('available_labels', labelNames.join(','));
78+
console.log(`Found ${labelNames.length} labels:`, labelNames);
79+
return labelNames;
80+
81+
- name: 'Run Gemini Issue Analysis'
6782
if: |-
6883
${{ steps.find_issues.outputs.issues_to_triage != '[]' }}
6984
uses: './'
70-
id: 'gemini_issue_triage'
85+
id: 'gemini_issue_analysis'
7186
env:
72-
GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
7387
ISSUES_TO_TRIAGE: '${{ steps.find_issues.outputs.issues_to_triage }}'
7488
REPOSITORY: '${{ github.repository }}'
89+
AVAILABLE_LABELS: '${{ steps.get_labels.outputs.available_labels }}'
7590
with:
7691
gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}'
7792
gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}'
@@ -85,10 +100,7 @@ jobs:
85100
{
86101
"maxSessionTurns": 25,
87102
"coreTools": [
88-
"run_shell_command(echo)",
89-
"run_shell_command(gh label list)",
90-
"run_shell_command(gh issue edit)",
91-
"run_shell_command(gh issue list)"
103+
"run_shell_command(echo)"
92104
],
93105
"telemetry": {
94106
"enabled": true,
@@ -98,26 +110,100 @@ jobs:
98110
prompt: |-
99111
## Role
100112
101-
You are an issue triage assistant. Analyze issues and apply
102-
appropriate labels. Use the available tools to gather information;
103-
do not ask for information to be provided.
113+
You are an issue triage assistant. Analyze the GitHub issues and
114+
identify the most appropriate existing labels to apply.
104115
105116
## Steps
106117
107-
1. Run: `gh label list`
108-
2. Check environment variable: "${ISSUES_TO_TRIAGE}" (JSON array
109-
of issues)
110-
3. For each issue, apply labels:
111-
`gh issue edit "${ISSUE_NUMBER}" --add-label "label1,label2"`.
112-
If available, set labels that follow the `kind/*`, `area/*`,
113-
and `priority/*` patterns.
114-
4. For each issue, if the `status/needs-triage` label is present,
115-
remove it using:
116-
`gh issue edit "${ISSUE_NUMBER}" --remove-label "status/needs-triage"`
118+
1. Review the available labels in the environment variable: "${AVAILABLE_LABELS}".
119+
2. Review the issues in the environment variable: "${ISSUES_TO_TRIAGE}".
120+
3. For each issue, classify it by the appropriate labels from the available labels.
121+
4. Output a JSON array of objects, each containing the issue number
122+
and the labels to add and remove. For example:
123+
```
124+
[
125+
{
126+
"issue_number": 123,
127+
"labels_to_add": ["kind/bug", "priority/p2"],
128+
"labels_to_remove": ["status/needs-triage"]
129+
},
130+
{
131+
"issue_number": 456,
132+
"labels_to_add": ["kind/enhancement"],
133+
"labels_to_remove": []
134+
}
135+
]
136+
```
137+
5. If an issue cannot be classified, do not include it in the output array.
117138
118139
## Guidelines
119140
120-
- Only use existing repository labels
121-
- Do not add comments
122-
- Triage each issue independently
141+
- Only use labels that already exist in the repository
142+
- Assign all applicable labels based on the issue content
123143
- Reference all shell variables as "${VAR}" (with quotes and braces)
144+
- Output only valid JSON format
145+
- Do not include any explanation or additional text, just the JSON
146+
147+
- name: 'Apply Labels to Issues'
148+
if: |-
149+
${{ steps.gemini_issue_analysis.outputs.summary != '[]' }}
150+
env:
151+
REPOSITORY: '${{ github.repository }}'
152+
LABELS_OUTPUT: '${{ steps.gemini_issue_analysis.outputs.summary }}'
153+
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
154+
with:
155+
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
156+
script: |-
157+
// Strip code block markers if present
158+
let labelsJson = process.env.LABELS_OUTPUT;
159+
labelsJson = labelsJson.replace(/^```(?:json)?\s*/, '').replace(/\s*```$/, '').trim();
160+
const triageResults = JSON.parse(labelsJson);
161+
console.log('Triage results:', triageResults);
162+
163+
for (const result of triageResults) {
164+
const issueNumber = result.issue_number;
165+
if (!issueNumber) {
166+
console.log('Skipping result with no issue number:', result);
167+
continue;
168+
}
169+
170+
// Apply labels
171+
if (result.labels_to_add && result.labels_to_add.length > 0) {
172+
await github.rest.issues.addLabels({
173+
owner: context.repo.owner,
174+
repo: context.repo.repo,
175+
issue_number: issueNumber,
176+
labels: result.labels_to_add
177+
});
178+
console.log(`Applied labels to #${issueNumber}: ${result.labels_to_add.join(', ')}`);
179+
}
180+
181+
// Remove labels
182+
if (result.labels_to_remove && result.labels_to_remove.length > 0) {
183+
for (const label of result.labels_to_remove) {
184+
try {
185+
await github.rest.issues.removeLabel({
186+
owner: context.repo.owner,
187+
repo: context.repo.repo,
188+
issue_number: issueNumber,
189+
name: label
190+
});
191+
console.log(`Removed label from #${issueNumber}: ${label}`);
192+
} catch (error) {
193+
// Label might not exist on the issue, which is fine
194+
console.log(`Could not remove label ${label} from #${issueNumber}: ${error.message}`);
195+
}
196+
}
197+
}
198+
}
199+
200+
- name: 'Post Issue Analysis Failure Comment'
201+
if: |-
202+
${{ failure() && steps.gemini_issue_analysis.outcome == 'failure' }}
203+
env:
204+
RUN_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
205+
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
206+
with:
207+
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
208+
script: |-
209+
console.error(`Gemini CLI issue triaging failed. Please check the [action logs](${process.env.RUN_URL}) for details.`);

0 commit comments

Comments
 (0)