@@ -54,15 +54,31 @@ jobs:
5454 app-id : ' ${{ vars.APP_ID }}'
5555 private-key : ' ${{ secrets.APP_PRIVATE_KEY }}'
5656
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'
5873 uses : ' ./'
59- id : ' gemini_issue_triage '
74+ id : ' gemini_issue_analysis '
6075 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
6277 ISSUE_TITLE : ' ${{ github.event.issue.title }}'
6378 ISSUE_BODY : ' ${{ github.event.issue.body }}'
6479 ISSUE_NUMBER : ' ${{ github.event.issue.number }}'
6580 REPOSITORY : ' ${{ github.repository }}'
81+ AVAILABLE_LABELS : ' ${{ steps.get_labels.outputs.available_labels }}'
6682 with :
6783 gemini_cli_version : ' ${{ vars.GEMINI_CLI_VERSION }}'
6884 gcp_workload_identity_provider : ' ${{ vars.GCP_WIF_PROVIDER }}'
7793 "debug": ${{ fromJSON(env.DEBUG || env.ACTIONS_STEP_DEBUG || false) }},
7894 "maxSessionTurns": 25,
7995 "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)"
8397 ],
8498 "telemetry": {
8599 "enabled": true,
@@ -90,41 +104,103 @@ jobs:
90104 ## Role
91105
92106 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
94108 tools to gather information; do not ask for information to be
95109 provided.
96110
97111 ## Steps
98112
99- 1. Run: `gh label list` to get all available labels .
113+ 1. Review the available labels in the environment variable: "${AVAILABLE_LABELS}" .
100114 2. Review the issue title and body provided in the environment
101115 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+ ```
109125
110126 ## Guidelines
111127
112128 - 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
115129 - Assign all applicable labels based on the issue content
116130 - 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
117133
118- - name : ' Post Issue Triage Failure Comment '
134+ - name : ' Apply Labels to Issue '
119135 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 }}'
121197 uses : ' actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
122198 with :
123199 github-token : ' ${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
124200 script : |-
125201 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.'
130206 })
0 commit comments