@@ -54,15 +54,31 @@ jobs:
54
54
app-id : ' ${{ vars.APP_ID }}'
55
55
private-key : ' ${{ secrets.APP_PRIVATE_KEY }}'
56
56
57
- - name : ' Run Gemini Issue Triage'
57
+ - name : ' Get Repository Labels'
58
+ id : ' get_labels'
59
+ uses : ' actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
60
+ with :
61
+ github-token : ' ${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
62
+ script : |-
63
+ const { data: labels } = await github.rest.issues.listLabelsForRepo({
64
+ owner: context.repo.owner,
65
+ repo: context.repo.repo,
66
+ });
67
+ const labelNames = labels.map(label => label.name);
68
+ core.setOutput('available_labels', labelNames.join(','));
69
+ core.info(`Found ${labelNames.length} labels: ${labelNames.join(', ')}`);
70
+ return labelNames;
71
+
72
+ - name : ' Run Gemini Issue Analysis'
58
73
uses : ' ./'
59
- id : ' gemini_issue_triage '
74
+ id : ' gemini_issue_analysis '
60
75
env :
61
- GITHUB_TOKEN : ' ${{ steps.generate_token.outputs. token || secrets.GITHUB_TOKEN }} '
76
+ GITHUB_TOKEN : ' ' # Do not pass any auth token here since this runs on untrusted inputs
62
77
ISSUE_TITLE : ' ${{ github.event.issue.title }}'
63
78
ISSUE_BODY : ' ${{ github.event.issue.body }}'
64
79
ISSUE_NUMBER : ' ${{ github.event.issue.number }}'
65
80
REPOSITORY : ' ${{ github.repository }}'
81
+ AVAILABLE_LABELS : ' ${{ steps.get_labels.outputs.available_labels }}'
66
82
with :
67
83
gemini_cli_version : ' ${{ vars.GEMINI_CLI_VERSION }}'
68
84
gcp_workload_identity_provider : ' ${{ vars.GCP_WIF_PROVIDER }}'
77
93
"debug": ${{ fromJSON(env.DEBUG || env.ACTIONS_STEP_DEBUG || false) }},
78
94
"maxSessionTurns": 25,
79
95
"coreTools": [
80
- "run_shell_command(echo)",
81
- "run_shell_command(gh label list)",
82
- "run_shell_command(gh issue edit)"
96
+ "run_shell_command(echo)"
83
97
],
84
98
"telemetry": {
85
99
"enabled": true,
@@ -90,41 +104,103 @@ jobs:
90
104
## Role
91
105
92
106
You are an issue triage assistant. Analyze the current GitHub issue
93
- and apply the most appropriate existing labels. Use the available
107
+ and identify the most appropriate existing labels. Use the available
94
108
tools to gather information; do not ask for information to be
95
109
provided.
96
110
97
111
## Steps
98
112
99
- 1. Run: `gh label list` to get all available labels .
113
+ 1. Review the available labels in the environment variable: "${AVAILABLE_LABELS}" .
100
114
2. Review the issue title and body provided in the environment
101
115
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"`
116
+ 3. Classify the issue by the appropriate labels from the available labels.
117
+ 4. Output the appropriate labels for this issue in JSON format, for example:
118
+ ```
119
+ {"labels_to_add": ["kind/bug", "priority/p2"], "labels_to_remove": ["status/needs-triage"]}
120
+ ```
121
+ 5. If the issue cannot be classified using the available labels, output:
122
+ ```
123
+ {"labels_to_add": [], "labels_to_remove": []}
124
+ ```
109
125
110
126
## Guidelines
111
127
112
128
- 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
115
129
- Assign all applicable labels based on the issue content
116
130
- Reference all shell variables as "${VAR}" (with quotes and braces)
131
+ - Output only valid JSON format
132
+ - Do not include any explanation or additional text, just the JSON
117
133
118
- - name : ' Post Issue Triage Failure Comment '
134
+ - name : ' Apply Labels to Issue '
119
135
if : |-
120
- ${{ failure() && steps.gemini_issue_triage.outcome == 'failure' }}
136
+ ${{ steps.gemini_issue_analysis.outputs.summary != '' }}
137
+ env :
138
+ REPOSITORY : ' ${{ github.repository }}'
139
+ ISSUE_NUMBER : ' ${{ github.event.issue.number }}'
140
+ LABELS_OUTPUT : ' ${{ steps.gemini_issue_analysis.outputs.summary }}'
141
+ uses : ' actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
142
+ with :
143
+ github-token : ' ${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
144
+ script : |-
145
+ // Strip code block markers if present
146
+ const rawLabels = process.env.LABELS_OUTPUT;
147
+ core.info(`Raw labels JSON: ${rawLabels}`);
148
+ let parsedLabels;
149
+ try {
150
+ const trimmedLabels = rawLabels.replace(/^```(?:json)?\s*/, '').replace(/\s*```$/, '').trim();
151
+ parsedLabels = JSON.parse(trimmedLabels);
152
+ core.info(`Parsed labels JSON: ${JSON.stringify(parsedLabels)}`);
153
+ } catch (err) {
154
+ core.setFailed(`Failed to parse labels JSON from Gemini output: ${err.message}\nRaw output: ${rawLabels}`);
155
+ return;
156
+ }
157
+
158
+ const issueNumber = parseInt(process.env.ISSUE_NUMBER);
159
+
160
+ // Apply labels
161
+ if (parsedLabels.labels_to_add && parsedLabels.labels_to_add.length > 0) {
162
+ core.info(`Adding labels to #${issueNumber}: ${parsedLabels.labels_to_add.join(', ')}`)
163
+ await github.rest.issues.addLabels({
164
+ owner: context.repo.owner,
165
+ repo: context.repo.repo,
166
+ issue_number: issueNumber,
167
+ labels: parsedLabels.labels_to_add
168
+ });
169
+ core.info(`Successfully applied labels to #${issueNumber}: ${parsedLabels.labels_to_add.join(', ')}`);
170
+ }
171
+
172
+ // Remove labels
173
+ if (parsedLabels.labels_to_remove && parsedLabels.labels_to_remove.length > 0) {
174
+ core.info(`Removing labels from #${issueNumber}: ${parsedLabels.labels_to_remove.join(', ')}`)
175
+ for (const label of parsedLabels.labels_to_remove) {
176
+ try {
177
+ await github.rest.issues.removeLabel({
178
+ owner: context.repo.owner,
179
+ repo: context.repo.repo,
180
+ issue_number: issueNumber,
181
+ name: label
182
+ });
183
+ core.info(`Successfully removed label from #${issueNumber}: ${label}`);
184
+ } catch (error) {
185
+ // Label might not exist on the issue, which is fine
186
+ core.info(`Could not remove label ${label} from #${issueNumber}: ${error.message}`);
187
+ }
188
+ }
189
+ }
190
+
191
+ - name : ' Post Issue Analysis Failure Comment'
192
+ if : |-
193
+ ${{ failure() && steps.gemini_issue_analysis.outcome == 'failure' }}
194
+ env :
195
+ ISSUE_NUMBER : ' ${{ github.event.issue.number }}'
196
+ RUN_URL : ' ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
121
197
uses : ' actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
122
198
with :
123
199
github-token : ' ${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
124
200
script : |-
125
201
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.'
202
+ owner: context.repo.owner ,
203
+ repo: context.repo.repo ,
204
+ issue_number: parseInt(process.env.ISSUE_NUMBER) ,
205
+ body: 'There is a problem with the Gemini CLI issue triaging. Please check the [action logs](${process.env.RUN_URL }) for details.'
130
206
})
0 commit comments