mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-02-01 22:48:03 +00:00
325 lines
15 KiB
YAML
325 lines
15 KiB
YAML
name: '🏷️ Gemini Automated Issue Triage'
|
|
|
|
on:
|
|
issues:
|
|
types:
|
|
- 'opened'
|
|
- 'reopened'
|
|
issue_comment:
|
|
types:
|
|
- 'created'
|
|
workflow_dispatch:
|
|
inputs:
|
|
issue_number:
|
|
description: 'issue number to triage'
|
|
required: true
|
|
type: 'number'
|
|
workflow_call:
|
|
inputs:
|
|
issue_number:
|
|
description: 'issue number to triage'
|
|
required: false
|
|
type: 'string'
|
|
|
|
concurrency:
|
|
group: '${{ github.workflow }}-${{ github.event.issue.number || github.event.inputs.issue_number || inputs.issue_number }}'
|
|
cancel-in-progress: true
|
|
|
|
defaults:
|
|
run:
|
|
shell: 'bash'
|
|
|
|
permissions:
|
|
contents: 'read'
|
|
id-token: 'write'
|
|
issues: 'write'
|
|
statuses: 'write'
|
|
packages: 'read'
|
|
actions: 'write' # Required for cancelling a workflow run
|
|
|
|
jobs:
|
|
triage-issue:
|
|
if: |-
|
|
(github.repository == 'google-gemini/gemini-cli' || github.repository == 'google-gemini/maintainers-gemini-cli') &&
|
|
(
|
|
github.event_name == 'workflow_dispatch' ||
|
|
(
|
|
(github.event_name == 'issues' || github.event_name == 'issue_comment') &&
|
|
(github.event_name != 'issue_comment' || (
|
|
contains(github.event.comment.body, '@gemini-cli /triage') &&
|
|
(github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'COLLABORATOR')
|
|
))
|
|
)
|
|
) &&
|
|
!contains(github.event.issue.labels.*.name, 'area/')
|
|
timeout-minutes: 5
|
|
runs-on: 'ubuntu-latest'
|
|
steps:
|
|
- name: 'Get issue data for manual trigger'
|
|
id: 'get_issue_data'
|
|
if: |-
|
|
github.event_name == 'workflow_dispatch'
|
|
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
|
|
with:
|
|
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
|
script: |
|
|
const issueNumber = ${{ github.event.inputs.issue_number || inputs.issue_number }};
|
|
const { data: issue } = await github.rest.issues.get({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: issueNumber,
|
|
});
|
|
core.setOutput('title', issue.title);
|
|
core.setOutput('body', issue.body);
|
|
core.setOutput('labels', issue.labels.map(label => label.name).join(','));
|
|
return issue;
|
|
|
|
- name: 'Manual Trigger Pre-flight Checks'
|
|
if: |-
|
|
github.event_name == 'workflow_dispatch'
|
|
env:
|
|
ISSUE_NUMBER_INPUT: '${{ github.event.inputs.issue_number || inputs.issue_number }}'
|
|
LABELS: '${{ steps.get_issue_data.outputs.labels }}'
|
|
run: |
|
|
if echo "${LABELS}" | grep -q 'area/'; then
|
|
echo "Issue #${ISSUE_NUMBER_INPUT} already has 'area/' label. Stopping workflow."
|
|
exit 1
|
|
fi
|
|
|
|
echo "Manual triage checks passed."
|
|
|
|
- name: 'Checkout'
|
|
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
|
|
|
- name: 'Generate GitHub App Token'
|
|
id: 'generate_token'
|
|
env:
|
|
APP_ID: '${{ secrets.APP_ID }}'
|
|
if: |-
|
|
${{ env.APP_ID != '' }}
|
|
uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2
|
|
with:
|
|
app-id: '${{ secrets.APP_ID }}'
|
|
private-key: '${{ secrets.PRIVATE_KEY }}'
|
|
permission-issues: 'write'
|
|
|
|
- name: 'Get Repository Labels'
|
|
id: 'get_labels'
|
|
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
|
|
with:
|
|
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
|
|
script: |-
|
|
const { data: labels } = await github.rest.issues.listLabelsForRepo({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
});
|
|
const allowedLabels = [
|
|
'area/agent',
|
|
'area/enterprise',
|
|
'area/non-interactive',
|
|
'area/core',
|
|
'area/security',
|
|
'area/platform',
|
|
'area/extensions',
|
|
'area/unknown'
|
|
];
|
|
const labelNames = labels.map(label => label.name).filter(name => allowedLabels.includes(name));
|
|
core.setOutput('available_labels', labelNames.join(','));
|
|
core.info(`Found ${labelNames.length} labels: ${labelNames.join(', ')}`);
|
|
return labelNames;
|
|
|
|
- name: 'Run Gemini Issue Analysis'
|
|
uses: 'google-github-actions/run-gemini-cli@a3bf79042542528e91937b3a3a6fbc4967ee3c31' # ratchet:google-github-actions/run-gemini-cli@v0
|
|
id: 'gemini_issue_analysis'
|
|
env:
|
|
GITHUB_TOKEN: '' # Do not pass any auth token here since this runs on untrusted inputs
|
|
ISSUE_TITLE: >-
|
|
${{ github.event_name == 'workflow_dispatch' && steps.get_issue_data.outputs.title || github.event.issue.title }}
|
|
ISSUE_BODY: >-
|
|
${{ github.event_name == 'workflow_dispatch' && steps.get_issue_data.outputs.body || github.event.issue.body }}
|
|
ISSUE_NUMBER: >-
|
|
${{ github.event_name == 'workflow_dispatch' && (github.event.inputs.issue_number || inputs.issue_number) || github.event.issue.number }}
|
|
REPOSITORY: '${{ github.repository }}'
|
|
AVAILABLE_LABELS: '${{ steps.get_labels.outputs.available_labels }}'
|
|
with:
|
|
gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}'
|
|
gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}'
|
|
gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}'
|
|
gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}'
|
|
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
|
use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}'
|
|
use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}'
|
|
settings: |-
|
|
{
|
|
"maxSessionTurns": 25,
|
|
"telemetry": {
|
|
"enabled": true,
|
|
"target": "gcp"
|
|
}
|
|
}
|
|
prompt: |-
|
|
## Role
|
|
|
|
You are an issue triage assistant. Your role is to analyze a GitHub issue and determine the single most appropriate area/ label based on the definitions provided.
|
|
|
|
## Steps
|
|
1. Review the issue title and body: ${{ env.ISSUE_TITLE }} and ${{ env.ISSUE_BODY }}.
|
|
2. Review the available labels: ${{ env.AVAILABLE_LABELS }}.
|
|
3. Select exactly one area/ label that best matches the issue based on Reference 1: Area Definitions.
|
|
4. Fallback Logic:
|
|
- If you cannot confidently determine the correct area/ label from the definitions, you must use area/unknown.
|
|
5. Output your selected label in JSON format and nothing else. Example:
|
|
{"labels_to_set": ["area/core"]}
|
|
|
|
## Guidelines
|
|
- Your output must contain exactly one area/ label.
|
|
- Triage only the current issue based on its title and body.
|
|
- Output only valid JSON format.
|
|
- Do not include any explanation or additional text, just the JSON.
|
|
|
|
Reference 1: Area Definitions
|
|
area/agent
|
|
- Description: Issues related to the "brain" of the CLI. This includes the core agent logic, model quality, tool/function calling, and memory.
|
|
- Example Issues:
|
|
"I am not getting a reasonable or expected response."
|
|
"The model is not calling the tool I expected."
|
|
"The web search tool is not working as expected."
|
|
"Feature request for a new built-in tool (e.g., read file, write file)."
|
|
"The generated code is poor quality or incorrect."
|
|
"The model seems stuck in a loop."
|
|
"The response from the model is malformed (e.g., broken JSON, bad formatting)."
|
|
"Concerns about unnecessary token consumption."
|
|
"Issues with how memory or chat history is managed."
|
|
"Issues with sub-agents."
|
|
"Model is switching from one to another unexpectedly."
|
|
|
|
area/enterprise
|
|
- Description: Issues specific to enterprise-level features, including telemetry, policy, and licenses.
|
|
- Example Issues:
|
|
"Usage data is not appearing in our telemetry dashboard."
|
|
"A user is able to perform an action that should be blocked by an admin policy."
|
|
"Questions about billing, licensing tiers, or enterprise quotas."
|
|
|
|
area/non-interactive
|
|
- Description: Issues related to using the CLI in automated or non-interactive environments (headless mode).
|
|
- Example Issues:
|
|
"Problems using the CLI as an SDK in another surface."
|
|
"The CLI is behaving differently when run from a shell script vs. an interactive terminal."
|
|
"GitHub action is failing."
|
|
"I am having trouble running the CLI in headless mode"
|
|
|
|
area/core
|
|
- Description: Issues with the fundamental CLI app itself. This includes the user interface (UI/UX), installation, OS compatibility, and performance.
|
|
- Example Issues:
|
|
"I am seeing my screen flicker when using the CLI."
|
|
"The output in my terminal is malformed or unreadable."
|
|
"Theme changes are not taking effect."
|
|
"Keyboard inputs (e.g., arrow keys, Ctrl+C) are not being recognized."
|
|
"The CLI failed to install or update."
|
|
"An issue specific to running on Windows, macOS, or Linux."
|
|
"Problems with command parsing, flags, or argument handling."
|
|
"High CPU or memory usage by the CLI process."
|
|
"Issues related to multi-modality (e.g., handling image inputs)."
|
|
"Problems with the IDE integration connection or installation"
|
|
|
|
area/security
|
|
- Description: Issues related to user authentication, authorization, data security, and privacy.
|
|
- Example Issues:
|
|
"I am unable to sign in."
|
|
"The login flow is selecting the wrong authentication path"
|
|
"Problems with API key handling or credential storage."
|
|
"A report of a security vulnerability"
|
|
"Concerns about data sanitization or potential data leaks."
|
|
"Issues or requests related to privacy controls."
|
|
"Preventing unauthorized data access."
|
|
|
|
area/platform
|
|
- Description: Issues related to CI/CD, release management, testing, eval infrastructure, capacity, quota management, and sandbox environments.
|
|
- Example Issues:
|
|
"I am getting a 429 'Resource Exhausted' or 500-level server error."
|
|
"General slowness or high latency from the service."
|
|
"The build script is broken on the main branch."
|
|
"Tests are failing in the CI/CD pipeline."
|
|
"Issues with the release management or publishing process."
|
|
"User is running out of capacity."
|
|
"Problems specific to the sandbox or staging environments."
|
|
"Questions about quota limits or requests for increases."
|
|
|
|
area/extensions
|
|
- Description: Issues related to the extension ecosystem, including the marketplace and website.
|
|
- Example Issues:
|
|
"Bugs related to the extension marketplace website."
|
|
"Issues with a specific extension."
|
|
"Feature request for the extension ecosystem."
|
|
|
|
area/unknown
|
|
- Description: Issues that do not clearly fit into any other defined area/ category, or where information is too limited to make a determination. Use this when no other area is appropriate.
|
|
|
|
- name: 'Apply Labels to Issue'
|
|
if: |-
|
|
${{ steps.gemini_issue_analysis.outputs.summary != '' }}
|
|
env:
|
|
REPOSITORY: '${{ github.repository }}'
|
|
ISSUE_NUMBER: '${{ github.event.issue.number || github.event.inputs.issue_number }}'
|
|
LABELS_OUTPUT: '${{ steps.gemini_issue_analysis.outputs.summary }}'
|
|
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
|
|
with:
|
|
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
|
|
script: |
|
|
const rawOutput = process.env.LABELS_OUTPUT;
|
|
core.info(`Raw output from model: ${rawOutput}`);
|
|
let parsedLabels;
|
|
try {
|
|
// First, try to parse the raw output as JSON.
|
|
parsedLabels = JSON.parse(rawOutput);
|
|
} catch (jsonError) {
|
|
// If that fails, check for a markdown code block.
|
|
core.warning(`Direct JSON parsing failed: ${jsonError.message}. Trying to extract from a markdown block.`);
|
|
const jsonMatch = rawOutput.match(/```json\s*([\s\S]*?)\s*```/);
|
|
if (jsonMatch && jsonMatch[1]) {
|
|
try {
|
|
parsedLabels = JSON.parse(jsonMatch[1].trim());
|
|
} catch (markdownError) {
|
|
core.setFailed(`Failed to parse JSON even after extracting from markdown block: ${markdownError.message}\nRaw output: ${rawOutput}`);
|
|
return;
|
|
}
|
|
} else {
|
|
core.setFailed(`Output is not valid JSON and does not contain a JSON markdown block.\nRaw output: ${rawOutput}`);
|
|
return;
|
|
}
|
|
}
|
|
|
|
const issueNumber = parseInt(process.env.ISSUE_NUMBER);
|
|
const labelsToAdd = parsedLabels.labels_to_set || [];
|
|
|
|
if (labelsToAdd.length !== 1) {
|
|
core.setFailed(`Expected exactly 1 label (area/), but got ${labelsToAdd.length}. Labels: ${labelsToAdd.join(', ')}`);
|
|
return;
|
|
}
|
|
|
|
// Set labels based on triage result
|
|
await github.rest.issues.addLabels({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: issueNumber,
|
|
labels: labelsToAdd
|
|
});
|
|
core.info(`Successfully added labels for #${issueNumber}: ${labelsToAdd.join(', ')}`);
|
|
|
|
- name: 'Post Issue Analysis Failure Comment'
|
|
if: |-
|
|
${{ failure() && steps.gemini_issue_analysis.outcome == 'failure' }}
|
|
env:
|
|
ISSUE_NUMBER: '${{ github.event.issue.number || github.event.inputs.issue_number }}'
|
|
RUN_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
|
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
|
|
with:
|
|
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
|
|
script: |-
|
|
github.rest.issues.createComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: parseInt(process.env.ISSUE_NUMBER),
|
|
body: 'There is a problem with the Gemini CLI issue triaging. Please check the [action logs](${process.env.RUN_URL}) for details.'
|
|
})
|