|
| 1 | +name: '💬 Gemini CLI' |
| 2 | + |
| 3 | +on: |
| 4 | + issue_comment: |
| 5 | + types: [created] |
| 6 | + |
| 7 | +concurrency: |
| 8 | + group: '${{ github.workflow }}-${{ github.event.issue.number }}' |
| 9 | + cancel-in-progress: |- |
| 10 | + ${{ github.event.sender.type == 'User' && ( github.event.issue.author_association == 'OWNER' || github.event.issue.author_association == 'MEMBER' || github.event.issue.author_association == 'COLLABORATOR') }} |
| 11 | +
|
| 12 | +defaults: |
| 13 | + run: |
| 14 | + shell: 'bash' |
| 15 | + |
| 16 | +permissions: |
| 17 | + contents: 'write' |
| 18 | + id-token: 'write' |
| 19 | + pull-requests: 'write' |
| 20 | + issues: 'write' |
| 21 | + |
| 22 | +jobs: |
| 23 | + gemini-cli: |
| 24 | + # This condition is complex to ensure we only run when explicitly invoked. |
| 25 | + if: |- |
| 26 | + github.event_name == 'workflow_dispatch' || |
| 27 | + ( |
| 28 | + github.event_name == 'issues' && github.event.action == 'opened' && |
| 29 | + contains(github.event.issue.body, '@gemini-cli') && |
| 30 | + !contains(github.event.issue.body, '@gemini-cli /review') && |
| 31 | + !contains(github.event.issue.body, '@gemini-cli /triage') && |
| 32 | + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.issue.author_association) |
| 33 | + ) || |
| 34 | + ( |
| 35 | + github.event_name == 'issue_comment' && |
| 36 | + contains(github.event.comment.body, '@gemini-cli') && |
| 37 | + !contains(github.event.comment.body, '@gemini-cli /review') && |
| 38 | + !contains(github.event.comment.body, '@gemini-cli /triage') && |
| 39 | + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) |
| 40 | + ) |
| 41 | + timeout-minutes: 10 |
| 42 | + runs-on: 'ubuntu-latest' |
| 43 | + |
| 44 | + steps: |
| 45 | + - name: 'Get context from event' |
| 46 | + id: 'get_context' |
| 47 | + env: |
| 48 | + EVENT_NAME: '${{ github.event_name }}' |
| 49 | + EVENT_PAYLOAD: '${{ toJSON(github.event) }}' |
| 50 | + run: |- |
| 51 | + set -euo pipefail |
| 52 | +
|
| 53 | + USER_REQUEST="" |
| 54 | + ISSUE_NUMBER="" |
| 55 | + IS_PR="false" |
| 56 | +
|
| 57 | + if [[ "${EVENT_NAME}" == "issues" ]]; then |
| 58 | + USER_REQUEST=$(echo "${EVENT_PAYLOAD}" | jq -r .issue.body) |
| 59 | + ISSUE_NUMBER=$(echo "${EVENT_PAYLOAD}" | jq -r .issue.number) |
| 60 | + elif [[ "${EVENT_NAME}" == "issue_comment" ]]; then |
| 61 | + USER_REQUEST=$(echo "${EVENT_PAYLOAD}" | jq -r .comment.body) |
| 62 | + ISSUE_NUMBER=$(echo "${EVENT_PAYLOAD}" | jq -r .issue.number) |
| 63 | + if [[ $(echo "${EVENT_PAYLOAD}" | jq -r .issue.pull_request) != "null" ]]; then |
| 64 | + IS_PR="true" |
| 65 | + fi |
| 66 | + fi |
| 67 | +
|
| 68 | + # Clean up user request |
| 69 | + USER_REQUEST=$(echo "${USER_REQUEST}" | sed 's/.*@gemini-cli//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') |
| 70 | +
|
| 71 | + { |
| 72 | + echo "user_request=${USER_REQUEST}" |
| 73 | + echo "issue_number=${ISSUE_NUMBER}" |
| 74 | + echo "is_pr=${IS_PR}" |
| 75 | + } >> "${GITHUB_OUTPUT}" |
| 76 | +
|
| 77 | + - name: 'Set up git user for commits' |
| 78 | + run: |- |
| 79 | + git config --global user.name 'gemini-cli[bot]' |
| 80 | + git config --global user.email 'gemini-cli[bot]@users.noreply.github.com' |
| 81 | +
|
| 82 | + - name: 'Checkout PR branch' |
| 83 | + if: |- |
| 84 | + ${{ steps.get_context.outputs.is_pr == 'true' }} |
| 85 | + uses: 'actions/checkout@v5' |
| 86 | + with: |
| 87 | + token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' |
| 88 | + repository: '${{ github.repository }}' |
| 89 | + ref: 'refs/pull/${{ steps.get_context.outputs.issue_number }}/head' |
| 90 | + fetch-depth: 0 |
| 91 | + |
| 92 | + - name: 'Checkout main branch' |
| 93 | + if: |- |
| 94 | + ${{ steps.get_context.outputs.is_pr == 'false' }} |
| 95 | + uses: 'actions/checkout@v5' |
| 96 | + with: |
| 97 | + token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' |
| 98 | + repository: '${{ github.repository }}' |
| 99 | + fetch-depth: 0 |
| 100 | + |
| 101 | + - name: 'Acknowledge request' |
| 102 | + env: |
| 103 | + GITHUB_ACTOR: '${{ github.actor }}' |
| 104 | + GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' |
| 105 | + ISSUE_NUMBER: '${{ steps.get_context.outputs.issue_number }}' |
| 106 | + REPOSITORY: '${{ github.repository }}' |
| 107 | + REQUEST_TYPE: '${{ steps.get_context.outputs.request_type }}' |
| 108 | + run: |- |
| 109 | + set -euo pipefail |
| 110 | + MESSAGE="@${GITHUB_ACTOR} I've received your request and I'm working on it now! 🤖" |
| 111 | + if [[ -n "${MESSAGE}" ]]; then |
| 112 | + gh issue comment "${ISSUE_NUMBER}" \ |
| 113 | + --body "${MESSAGE}" \ |
| 114 | + --repo "${REPOSITORY}" |
| 115 | + fi |
| 116 | +
|
| 117 | + - name: 'Get description' |
| 118 | + id: 'get_description' |
| 119 | + env: |
| 120 | + GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' |
| 121 | + IS_PR: '${{ steps.get_context.outputs.is_pr }}' |
| 122 | + ISSUE_NUMBER: '${{ steps.get_context.outputs.issue_number }}' |
| 123 | + run: |- |
| 124 | + set -euo pipefail |
| 125 | + if [[ "${IS_PR}" == "true" ]]; then |
| 126 | + DESCRIPTION=$(gh pr view "${ISSUE_NUMBER}" --json body --template '{{.body}}') |
| 127 | + else |
| 128 | + DESCRIPTION=$(gh issue view "${ISSUE_NUMBER}" --json body --template '{{.body}}') |
| 129 | + fi |
| 130 | + { |
| 131 | + echo "description<<EOF" |
| 132 | + echo "${DESCRIPTION}" |
| 133 | + echo "EOF" |
| 134 | + } >> "${GITHUB_OUTPUT}" |
| 135 | +
|
| 136 | + - name: 'Get comments' |
| 137 | + id: 'get_comments' |
| 138 | + env: |
| 139 | + GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' |
| 140 | + IS_PR: '${{ steps.get_context.outputs.is_pr }}' |
| 141 | + ISSUE_NUMBER: '${{ steps.get_context.outputs.issue_number }}' |
| 142 | + run: |- |
| 143 | + set -euo pipefail |
| 144 | + if [[ "${IS_PR}" == "true" ]]; then |
| 145 | + COMMENTS=$(gh pr view "${ISSUE_NUMBER}" --json comments --template '{{range .comments}}{{.author.login}}: {{.body}}{{"\n"}}{{end}}') |
| 146 | + else |
| 147 | + COMMENTS=$(gh issue view "${ISSUE_NUMBER}" --json comments --template '{{range .comments}}{{.author.login}}: {{.body}}{{"\n"}}{{end}}') |
| 148 | + fi |
| 149 | + { |
| 150 | + echo "comments<<EOF" |
| 151 | + echo "${COMMENTS}" |
| 152 | + echo "EOF" |
| 153 | + } >> "${GITHUB_OUTPUT}" |
| 154 | +
|
| 155 | + - name: 'Run Gemini' |
| 156 | + id: 'run_gemini' |
| 157 | + uses: 'google-github-actions/run-gemini-cli@v0' |
| 158 | + env: |
| 159 | + GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' |
| 160 | + REPOSITORY: '${{ github.repository }}' |
| 161 | + USER_REQUEST: '${{ steps.get_context.outputs.user_request }}' |
| 162 | + ISSUE_NUMBER: '${{ steps.get_context.outputs.issue_number }}' |
| 163 | + IS_PR: '${{ steps.get_context.outputs.is_pr }}' |
| 164 | + with: |
| 165 | + gemini_api_key: '${{ secrets.GEMINI_API_KEY }}' |
| 166 | + gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' |
| 167 | + gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}' |
| 168 | + gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}' |
| 169 | + gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}' |
| 170 | + use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' |
| 171 | + use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' |
| 172 | + settings: |- |
| 173 | + { |
| 174 | + "maxSessionTurns": 50, |
| 175 | + "telemetry": { |
| 176 | + "enabled": false, |
| 177 | + "target": "gcp" |
| 178 | + } |
| 179 | + } |
| 180 | + prompt: |- |
| 181 | + ## Role |
| 182 | +
|
| 183 | + You are a helpful AI assistant invoked via a CLI interface in a GitHub workflow. You have access to tools to interact with the repository and respond to the user. |
| 184 | +
|
| 185 | + ## Context |
| 186 | +
|
| 187 | + - **Repository**: `${{ github.repository }}` |
| 188 | + - **Triggering Event**: `${{ github.event_name }}` |
| 189 | + - **Issue/PR Number**: `${{ steps.get_context.outputs.issue_number }}` |
| 190 | + - **Is this a PR?**: `${{ steps.get_context.outputs.is_pr }}` |
| 191 | + - **Issue/PR Description**: |
| 192 | + `${{ steps.get_description.outputs.description }}` |
| 193 | + - **Comments**: |
| 194 | + `${{ steps.get_comments.outputs.comments }}` |
| 195 | +
|
| 196 | + ## User Request |
| 197 | +
|
| 198 | + The user has sent the following request: |
| 199 | + `${{ steps.get_context.outputs.user_request }}` |
| 200 | +
|
| 201 | + ## How to Respond to Issues, PR Comments, and Questions |
| 202 | +
|
| 203 | + This workflow supports three main scenarios: |
| 204 | +
|
| 205 | + 1. **Creating a Fix for an Issue** |
| 206 | + - Carefully read the user request and the related issue or PR description. |
| 207 | + - Use available tools to gather all relevant context (e.g., `gh issue view`, `gh pr view`, `gh pr diff`, `cat`, `head`, `tail`). |
| 208 | + - Identify the root cause of the problem before proceeding. |
| 209 | + - **Show and maintain a plan as a checklist**: |
| 210 | + - At the very beginning, outline the steps needed to resolve the issue or address the request and post them as a checklist comment on the issue or PR (use GitHub markdown checkboxes: `- [ ] Task`). |
| 211 | + - Example: |
| 212 | + ``` |
| 213 | + ### Plan |
| 214 | + - [ ] Investigate the root cause |
| 215 | + - [ ] Implement the fix in `file.py` |
| 216 | + - [ ] Add/modify tests |
| 217 | + - [ ] Update documentation |
| 218 | + - [ ] Verify the fix and close the issue |
| 219 | + ``` |
| 220 | + - Use: `gh pr comment "${ISSUE_NUMBER}" --body "<plan>"` or `gh issue comment "${ISSUE_NUMBER}" --body "<plan>"` to post the initial plan. |
| 221 | + - As you make progress, keep the checklist visible and up to date by editing the same comment (check off completed tasks with `- [x]`). |
| 222 | + - To update the checklist: |
| 223 | + 1. Find the comment ID for the checklist (use `gh pr comment list "${ISSUE_NUMBER}"` or `gh issue comment list "${ISSUE_NUMBER}"`). |
| 224 | + 2. Edit the comment with the updated checklist: |
| 225 | + - For PRs: `gh pr comment --edit <comment-id> --body "<updated plan>"` |
| 226 | + - For Issues: `gh issue comment --edit <comment-id> --body "<updated plan>"` |
| 227 | + 3. The checklist should only be maintained as a comment on the issue or PR. Do not track or update the checklist in code files. |
| 228 | + - If the fix requires code changes, determine which files and lines are affected. If clarification is needed, note any questions for the user. |
| 229 | + - Make the necessary code or documentation changes using the available tools (e.g., `write_file`). Ensure all changes follow project conventions and best practices. Reference all shell variables as `"${VAR}"` (with quotes and braces) to prevent errors. |
| 230 | + - Run any relevant tests or checks to verify the fix works as intended. If possible, provide evidence (test output, screenshots, etc.) that the issue is resolved. |
| 231 | + - **Branching and Committing**: |
| 232 | + - **NEVER commit directly to the `main` branch.** |
| 233 | + - If you are working on a **pull request** (`IS_PR` is `true`), the correct branch is already checked out. Simply commit and push to it. |
| 234 | + - `git add .` |
| 235 | + - `git commit -m "feat: <describe the change>"` |
| 236 | + - `git push` |
| 237 | + - If you are working on an **issue** (`IS_PR` is `false`), create a new branch for your changes. A good branch name would be `issue/${ISSUE_NUMBER}/<short-description>`. |
| 238 | + - `git checkout -b issue/${ISSUE_NUMBER}/my-fix` |
| 239 | + - `git add .` |
| 240 | + - `git commit -m "feat: <describe the fix>"` |
| 241 | + - `git push origin issue/${ISSUE_NUMBER}/my-fix` |
| 242 | + - After pushing, you can create a pull request: `gh pr create --title "Fixes #${ISSUE_NUMBER}: <short title>" --body "This PR addresses issue #${ISSUE_NUMBER}."` |
| 243 | + - Summarize what was changed and why in a markdown file: `write_file("response.md", "<your response here>")` |
| 244 | + - Post the response as a comment: |
| 245 | + - For PRs: `gh pr comment "${ISSUE_NUMBER}" --body-file response.md` |
| 246 | + - For Issues: `gh issue comment "${ISSUE_NUMBER}" --body-file response.md` |
| 247 | +
|
| 248 | + 2. **Addressing Comments on a Pull Request** |
| 249 | + - Read the specific comment and the context of the PR. |
| 250 | + - Use tools like `gh pr view`, `gh pr diff`, and `cat` to understand the code and discussion. |
| 251 | + - If the comment requests a change or clarification, follow the same process as for fixing an issue: create a checklist plan, implement, test, and commit any required changes, updating the checklist as you go. |
| 252 | + - **Committing Changes**: The correct PR branch is already checked out. Simply add, commit, and push your changes. |
| 253 | + - `git add .` |
| 254 | + - `git commit -m "fix: address review comments"` |
| 255 | + - `git push` |
| 256 | + - If the comment is a question, answer it directly and clearly, referencing code or documentation as needed. |
| 257 | + - Document your response in `response.md` and post it as a PR comment: `gh pr comment "${ISSUE_NUMBER}" --body-file response.md` |
| 258 | +
|
| 259 | + 3. **Answering Any Question on an Issue** |
| 260 | + - Read the question and the full issue context using `gh issue view` and related tools. |
| 261 | + - Research or analyze the codebase as needed to provide an accurate answer. |
| 262 | + - If the question requires code or documentation changes, follow the fix process above, including creating and updating a checklist plan and **creating a new branch for your changes as described in section 1.** |
| 263 | + - Write a clear, concise answer in `response.md` and post it as an issue comment: `gh issue comment "${ISSUE_NUMBER}" --body-file response.md` |
| 264 | +
|
| 265 | + ## Guidelines |
| 266 | +
|
| 267 | + - **Be concise and actionable.** Focus on solving the user's problem efficiently. |
| 268 | + - **Always commit and push your changes if you modify code or documentation.** |
| 269 | + - **If you are unsure about the fix or answer, explain your reasoning and ask clarifying questions.** |
| 270 | + - **Follow project conventions and best practices.** |
0 commit comments