From 624ad283432354e8c6b9ec377b98d9e71585bfaa Mon Sep 17 00:00:00 2001 From: callumhyoung Date: Fri, 10 Oct 2025 10:36:59 -0700 Subject: [PATCH 01/10] Normalize tool names to match run-gemini-cli GHA changes --- commands/security/analyze-github-pr.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/commands/security/analyze-github-pr.toml b/commands/security/analyze-github-pr.toml index 41e2f7c..867252e 100644 --- a/commands/security/analyze-github-pr.toml +++ b/commands/security/analyze-github-pr.toml @@ -111,9 +111,9 @@ You will now begin executing the plan. The following are your precise instructio - Retrieve the GitHub repository name from the environment variable "${REPOSITORY}". - Retrieve the GitHub pull request number from the environment variable "${PULL_REQUEST_NUMBER}". - Retrieve the additional user instructions and context from the environment variable "${ADDITIONAL_CONTEXT}". - - Use `mcp__github__get_pull_request` to get the title, body, and metadata about the pull request. - - Use `mcp__github__get_pull_request_files` to get the list of files that were added, removed, and changed in the pull request. - - Use `mcp__github__get_pull_request_diff` to get the diff from the pull request. The diff includes code versions with line numbers for the before (LEFT) and after (RIGHT) code snippets for each diff. + - Use `get_pull_request` to get the title, body, and metadata about the pull request. + - Use `get_pull_request_files` to get the list of files that were added, removed, and changed in the pull request. + - Use `get_pull_request_diff` to get the diff from the pull request. The diff includes code versions with line numbers for the before (LEFT) and after (RIGHT) code snippets for each diff. * Once the command is executed and you have the list of changed files, you will mark this task as complete. @@ -129,9 +129,9 @@ After completing these two initial tasks, continue executing the dynamically gen After your **Core Operational Loop** is completed, report the final report back to GitHub: - 3.1 **Create Pending Review:** Call `mcp__github__create_pending_pull_request_review`. Ignore errors like "can only have one pending review per pull request" and proceed to the next step. + 3.1 **Create Pending Review:** Call `create_pending_pull_request_review`. Ignore errors like "can only have one pending review per pull request" and proceed to the next step. - 3.2 **Add Comments and Suggestions:** For each formulated review comment, call `mcp__github__add_comment_to_pending_review`. + 3.2 **Add Comments and Suggestions:** For each formulated review comment, call `add_comment_to_pending_review`. 2a. When there is a code suggestion (preferred), structure the comment payload using this exact template: @@ -149,7 +149,7 @@ After completing these two initial tasks, continue executing the dynamically gen {{SEVERITY}} {{COMMENT_TEXT}} - 3.3 **Submit Final Review:** Call `mcp__github__submit_pending_pull_request_review` with a summary comment. **DO NOT** approve the pull request. **DO NOT** request changes. The summary comment **MUST** use this exact markdown format: + 3.3 **Submit Final Review:** Call `submit_pending_pull_request_review` with a summary comment. **DO NOT** approve the pull request. **DO NOT** request changes. The summary comment **MUST** use this exact markdown format: ## 📋 Security Analysis Summary From 89e687c8a70627f68210510fb4f1f674d24f5c8d Mon Sep 17 00:00:00 2001 From: callumhyoung Date: Mon, 13 Oct 2025 09:56:31 -0700 Subject: [PATCH 02/10] Refactor Github MCP PR reads after github mcp 0.18.0 release --- commands/security/analyze-github-pr.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/commands/security/analyze-github-pr.toml b/commands/security/analyze-github-pr.toml index 867252e..79fdcbe 100644 --- a/commands/security/analyze-github-pr.toml +++ b/commands/security/analyze-github-pr.toml @@ -111,9 +111,7 @@ You will now begin executing the plan. The following are your precise instructio - Retrieve the GitHub repository name from the environment variable "${REPOSITORY}". - Retrieve the GitHub pull request number from the environment variable "${PULL_REQUEST_NUMBER}". - Retrieve the additional user instructions and context from the environment variable "${ADDITIONAL_CONTEXT}". - - Use `get_pull_request` to get the title, body, and metadata about the pull request. - - Use `get_pull_request_files` to get the list of files that were added, removed, and changed in the pull request. - - Use `get_pull_request_diff` to get the diff from the pull request. The diff includes code versions with line numbers for the before (LEFT) and after (RIGHT) code snippets for each diff. + - Use `pull_request_read` to get the title, body, and metadata about the pull request, as well all information about files changes and the entire git diff. * Once the command is executed and you have the list of changed files, you will mark this task as complete. From a4a1d09ea34ed33eda667c571363e305b58f953a Mon Sep 17 00:00:00 2001 From: callumhyoung Date: Mon, 13 Oct 2025 10:02:46 -0700 Subject: [PATCH 03/10] Update MCP calls --- commands/security/analyze-github-pr.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/commands/security/analyze-github-pr.toml b/commands/security/analyze-github-pr.toml index 79fdcbe..4c241fd 100644 --- a/commands/security/analyze-github-pr.toml +++ b/commands/security/analyze-github-pr.toml @@ -111,7 +111,9 @@ You will now begin executing the plan. The following are your precise instructio - Retrieve the GitHub repository name from the environment variable "${REPOSITORY}". - Retrieve the GitHub pull request number from the environment variable "${PULL_REQUEST_NUMBER}". - Retrieve the additional user instructions and context from the environment variable "${ADDITIONAL_CONTEXT}". - - Use `pull_request_read` to get the title, body, and metadata about the pull request, as well all information about files changes and the entire git diff. + - Use `pull_request_read.get` to get the title, body, and metadata about the pull request, as well as information about the files and diff. + - Use `get_pull_request_files.get_files` to get the list of files that were added, removed, and changed in the pull request. + - Use `get_pull_request_diff.get_diff` to get the diff from the pull request. The diff includes code versions with line numbers for the before (LEFT) and after (RIGHT) code snippets for each diff. * Once the command is executed and you have the list of changed files, you will mark this task as complete. From 96d94732b26a72a8c1d2a9cc6b71fe8d1a3fe767 Mon Sep 17 00:00:00 2001 From: callumhyoung Date: Mon, 13 Oct 2025 10:26:43 -0700 Subject: [PATCH 04/10] Update commands --- commands/security/analyze-github-pr.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commands/security/analyze-github-pr.toml b/commands/security/analyze-github-pr.toml index 4c241fd..0dd6425 100644 --- a/commands/security/analyze-github-pr.toml +++ b/commands/security/analyze-github-pr.toml @@ -112,8 +112,8 @@ You will now begin executing the plan. The following are your precise instructio - Retrieve the GitHub pull request number from the environment variable "${PULL_REQUEST_NUMBER}". - Retrieve the additional user instructions and context from the environment variable "${ADDITIONAL_CONTEXT}". - Use `pull_request_read.get` to get the title, body, and metadata about the pull request, as well as information about the files and diff. - - Use `get_pull_request_files.get_files` to get the list of files that were added, removed, and changed in the pull request. - - Use `get_pull_request_diff.get_diff` to get the diff from the pull request. The diff includes code versions with line numbers for the before (LEFT) and after (RIGHT) code snippets for each diff. + - Use `pull_request_files.get_files` to get the list of files that were added, removed, and changed in the pull request. + - Use `pull_request_diff.get_diff` to get the diff from the pull request. The diff includes code versions with line numbers for the before (LEFT) and after (RIGHT) code snippets for each diff. * Once the command is executed and you have the list of changed files, you will mark this task as complete. From e7cdd07d61be53d0dcc11f691582719a5f8b0606 Mon Sep 17 00:00:00 2001 From: callumhyoung Date: Mon, 13 Oct 2025 11:28:05 -0700 Subject: [PATCH 05/10] Add .env to .gitignore and add GHA workflows --- .github/workflows/gemini-dispatch.yml | 185 ++++++++++++++++++++++++++ .github/workflows/gemini-invoke.yml | 122 +++++++++++++++++ .github/workflows/gemini-review.yml | 175 ++++++++++++++++++++++++ .gitignore | 4 +- 4 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/gemini-dispatch.yml create mode 100644 .github/workflows/gemini-invoke.yml create mode 100644 .github/workflows/gemini-review.yml diff --git a/.github/workflows/gemini-dispatch.yml b/.github/workflows/gemini-dispatch.yml new file mode 100644 index 0000000..1cc5f42 --- /dev/null +++ b/.github/workflows/gemini-dispatch.yml @@ -0,0 +1,185 @@ +name: '🔀 Gemini Dispatch' + +on: + pull_request_review_comment: + types: + - 'created' + pull_request_review: + types: + - 'submitted' + pull_request: + types: + - 'opened' + issues: + types: + - 'opened' + - 'reopened' + issue_comment: + types: + - 'created' + +defaults: + run: + shell: 'bash' + +jobs: + debugger: + if: |- + ${{ fromJSON(vars.DEBUG || vars.ACTIONS_STEP_DEBUG || false) }} + runs-on: 'ubuntu-latest' + permissions: + contents: 'read' + steps: + - name: 'Print context for debugging' + env: + DEBUG_event_name: '${{ github.event_name }}' + DEBUG_event__action: '${{ github.event.action }}' + DEBUG_event__comment__author_association: '${{ github.event.comment.author_association }}' + DEBUG_event__issue__author_association: '${{ github.event.issue.author_association }}' + DEBUG_event__pull_request__author_association: '${{ github.event.pull_request.author_association }}' + DEBUG_event__review__author_association: '${{ github.event.review.author_association }}' + DEBUG_event: '${{ toJSON(github.event) }}' + run: |- + env | grep '^DEBUG_' + + dispatch: + # For PRs: only if not from a fork + # For comments: only if user types @gemini-cli and is OWNER/MEMBER/COLLABORATOR + # For issues: only on open/reopen + if: |- + ( + github.event_name == 'pull_request' && + github.event.pull_request.head.repo.fork == false + ) || ( + github.event.sender.type == 'User' && + startsWith(github.event.comment.body || github.event.review.body || github.event.issue.body, '@gemini-cli') && + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association || github.event.review.author_association || github.event.issue.author_association) + ) || ( + github.event_name == 'issues' && + contains(fromJSON('["opened", "reopened"]'), github.event.action) + ) + runs-on: 'ubuntu-latest' + permissions: + contents: 'read' + issues: 'write' + pull-requests: 'write' + outputs: + command: '${{ steps.extract_command.outputs.command }}' + request: '${{ steps.extract_command.outputs.request }}' + additional_context: '${{ steps.extract_command.outputs.additional_context }}' + issue_number: '${{ github.event.pull_request.number || github.event.issue.number }}' + steps: + - name: 'Mint identity token' + id: 'mint_identity_token' + if: |- + ${{ vars.APP_ID }} + uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2 + with: + app-id: '${{ vars.APP_ID }}' + private-key: '${{ secrets.APP_PRIVATE_KEY }}' + permission-contents: 'read' + permission-issues: 'write' + permission-pull-requests: 'write' + + - name: 'Extract command' + id: 'extract_command' + uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea' # ratchet:actions/github-script@v7 + env: + EVENT_TYPE: '${{ github.event_name }}.${{ github.event.action }}' + REQUEST: '${{ github.event.comment.body || github.event.review.body || github.event.issue.body }}' + with: + script: | + const request = process.env.REQUEST; + const eventType = process.env.EVENT_TYPE + core.setOutput('request', request); + + if (request.startsWith("@gemini-cli /review")) { + core.setOutput('command', 'review'); + const additionalContext = request.replace(/^@gemini-cli \/review/, '').trim(); + core.setOutput('additional_context', additionalContext); + } else if (request.startsWith("@gemini-cli")) { + core.setOutput('command', 'invoke'); + const additionalContext = request.replace(/^@gemini-cli/, '').trim(); + core.setOutput('additional_context', additionalContext); + } else if (eventType === 'pull_request.opened') { + core.setOutput('command', 'review'); + } else { + core.setOutput('command', 'fallthrough'); + } + + - name: 'Acknowledge request' + env: + GITHUB_TOKEN: '${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}' + ISSUE_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' + MESSAGE: |- + 🤖 Hi @${{ github.actor }}, I've received your request, and I'm working on it now! You can track my progress [in the logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for more details. + REPOSITORY: '${{ github.repository }}' + run: |- + gh issue comment "${ISSUE_NUMBER}" \ + --body "${MESSAGE}" \ + --repo "${REPOSITORY}" + + review: + needs: 'dispatch' + if: |- + ${{ needs.dispatch.outputs.command == 'review' }} + uses: './.github/workflows/gemini-review.yml' + permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + pull-requests: 'write' + with: + additional_context: '${{ needs.dispatch.outputs.additional_context }}' + secrets: 'inherit' + + invoke: + needs: 'dispatch' + if: |- + ${{ needs.dispatch.outputs.command == 'invoke' }} + uses: './.github/workflows/gemini-invoke.yml' + permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + pull-requests: 'write' + with: + additional_context: '${{ needs.dispatch.outputs.additional_context }}' + secrets: 'inherit' + + fallthrough: + needs: + - 'dispatch' + - 'review' + - 'invoke' + if: |- + ${{ always() && !cancelled() && (failure() || needs.dispatch.outputs.command == 'fallthrough') }} + runs-on: 'ubuntu-latest' + permissions: + contents: 'read' + issues: 'write' + pull-requests: 'write' + steps: + - name: 'Mint identity token' + id: 'mint_identity_token' + if: |- + ${{ vars.APP_ID }} + uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2 + with: + app-id: '${{ vars.APP_ID }}' + private-key: '${{ secrets.APP_PRIVATE_KEY }}' + permission-contents: 'read' + permission-issues: 'write' + permission-pull-requests: 'write' + + - name: 'Send failure comment' + env: + GITHUB_TOKEN: '${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}' + ISSUE_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' + MESSAGE: |- + 🤖 I'm sorry @${{ github.actor }}, but I was unable to process your request. Please [see the logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for more details. + REPOSITORY: '${{ github.repository }}' + run: |- + gh issue comment "${ISSUE_NUMBER}" \ + --body "${MESSAGE}" \ + --repo "${REPOSITORY}" \ No newline at end of file diff --git a/.github/workflows/gemini-invoke.yml b/.github/workflows/gemini-invoke.yml new file mode 100644 index 0000000..8e06994 --- /dev/null +++ b/.github/workflows/gemini-invoke.yml @@ -0,0 +1,122 @@ +name: '▶️ Gemini Invoke' + +on: + workflow_call: + inputs: + additional_context: + type: 'string' + description: 'Any additional context from the request' + required: false + +concurrency: + group: '${{ github.workflow }}-invoke-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number }}' + cancel-in-progress: false + +defaults: + run: + shell: 'bash' + +jobs: + invoke: + runs-on: 'ubuntu-latest' + permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + pull-requests: 'write' + steps: + - name: 'Mint identity token' + id: 'mint_identity_token' + if: |- + ${{ vars.APP_ID }} + uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2 + with: + app-id: '${{ vars.APP_ID }}' + private-key: '${{ secrets.APP_PRIVATE_KEY }}' + permission-contents: 'read' + permission-issues: 'write' + permission-pull-requests: 'write' + + - name: 'Run Gemini CLI' + id: 'run_gemini' + uses: 'google-github-actions/run-gemini-cli@main' # ratchet:exclude + env: + TITLE: '${{ github.event.pull_request.title || github.event.issue.title }}' + DESCRIPTION: '${{ github.event.pull_request.body || github.event.issue.body }}' + EVENT_NAME: '${{ github.event_name }}' + GITHUB_TOKEN: '${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}' + IS_PULL_REQUEST: '${{ !!github.event.pull_request }}' + ISSUE_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' + REPOSITORY: '${{ github.repository }}' + ADDITIONAL_CONTEXT: '${{ inputs.additional_context }}' + with: + gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}' + gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}' + gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}' + gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' + gemini_api_key: '${{ secrets.GEMINI_API_KEY }}' + gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}' + gemini_debug: '${{ fromJSON(vars.DEBUG || vars.ACTIONS_STEP_DEBUG || false) }}' + gemini_model: '${{ vars.GEMINI_MODEL }}' + google_api_key: '${{ secrets.GOOGLE_API_KEY }}' + use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' + use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' + settings: |- + { + "model": { + "maxSessionTurns": 25 + }, + "telemetry": { + "enabled": ${{ vars.GOOGLE_CLOUD_PROJECT != '' }}, + "target": "gcp" + }, + "mcpServers": { + "github": { + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "GITHUB_PERSONAL_ACCESS_TOKEN", + "ghcr.io/github/github-mcp-server" + ], + "includeTools": [ + "add_issue_comment", + "get_issue", + "get_issue_comments", + "list_issues", + "search_issues", + "create_pull_request", + "get_pull_request", + "get_pull_request_comments", + "get_pull_request_diff", + "get_pull_request_files", + "list_pull_requests", + "search_pull_requests", + "create_branch", + "create_or_update_file", + "delete_file", + "fork_repository", + "get_commit", + "get_file_contents", + "list_commits", + "push_files", + "search_code" + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" + } + } + }, + "tools": { + "core": [ + "run_shell_command(cat)", + "run_shell_command(echo)", + "run_shell_command(grep)", + "run_shell_command(head)", + "run_shell_command(tail)" + ] + } + } + prompt: '/gemini-invoke' \ No newline at end of file diff --git a/.github/workflows/gemini-review.yml b/.github/workflows/gemini-review.yml new file mode 100644 index 0000000..ff46e5e --- /dev/null +++ b/.github/workflows/gemini-review.yml @@ -0,0 +1,175 @@ +name: '🔎 Gemini Review' + +on: + workflow_call: + inputs: + additional_context: + type: 'string' + description: 'Any additional context from the request' + required: false + +concurrency: + group: '${{ github.workflow }}-review-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number }}' + cancel-in-progress: true + +defaults: + run: + shell: 'bash' + +jobs: + review: + runs-on: 'ubuntu-latest' + timeout-minutes: 7 + permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + pull-requests: 'write' + steps: + - name: 'Mint identity token' + id: 'mint_identity_token' + if: |- + ${{ vars.APP_ID }} + uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2 + with: + app-id: '${{ vars.APP_ID }}' + private-key: '${{ secrets.APP_PRIVATE_KEY }}' + permission-contents: 'read' + permission-issues: 'write' + permission-pull-requests: 'write' + + - name: 'Checkout repository' + uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 + + - name: 'Run Gemini pull request review' + uses: 'google-github-actions/run-gemini-cli@main' # ratchet:exclude + id: 'gemini_pr_review' + env: + GITHUB_TOKEN: '${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}' + ISSUE_TITLE: '${{ github.event.pull_request.title || github.event.issue.title }}' + ISSUE_BODY: '${{ github.event.pull_request.body || github.event.issue.body }}' + PULL_REQUEST_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' + REPOSITORY: '${{ github.repository }}' + ADDITIONAL_CONTEXT: '${{ inputs.additional_context }}' + with: + gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}' + gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}' + gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}' + gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' + gemini_api_key: '${{ secrets.GEMINI_API_KEY }}' + gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}' + gemini_debug: '${{ fromJSON(vars.DEBUG || vars.ACTIONS_STEP_DEBUG || false) }}' + gemini_model: '${{ vars.GEMINI_MODEL }}' + google_api_key: '${{ secrets.GOOGLE_API_KEY }}' + use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' + use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' + settings: |- + { + "model": { + "maxSessionTurns": 25 + }, + "telemetry": { + "enabled": ${{ vars.GOOGLE_CLOUD_PROJECT != '' }}, + "target": "gcp" + }, + "mcpServers": { + "github": { + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "GITHUB_PERSONAL_ACCESS_TOKEN", + "ghcr.io/github/github-mcp-server" + ], + "includeTools": [ + "add_comment_to_pending_review", + "create_pending_pull_request_review", + "pull_request_read", + "submit_pending_pull_request_review" + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" + } + } + }, + "tools": { + "core": [ + "run_shell_command(cat)", + "run_shell_command(echo)", + "run_shell_command(grep)", + "run_shell_command(head)", + "run_shell_command(tail)" + ] + } + } + prompt: '/gemini-review' + - name: 'Run Gemini security analysis review' + uses: 'google-github-actions/run-gemini-cli@main' # ratchet:exclude + id: 'gemini_security_analysis' + env: + GITHUB_TOKEN: '${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}' + ISSUE_TITLE: '${{ github.event.pull_request.title || github.event.issue.title }}' + ISSUE_BODY: '${{ github.event.pull_request.body || github.event.issue.body }}' + PULL_REQUEST_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' + REPOSITORY: '${{ github.repository }}' + ADDITIONAL_CONTEXT: '${{ inputs.additional_context }}' + with: + gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}' + gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}' + gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}' + gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' + gemini_api_key: '${{ secrets.GEMINI_API_KEY }}' + gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}' + gemini_debug: '${{ fromJSON(vars.DEBUG || vars.ACTIONS_STEP_DEBUG || false) }}' + gemini_model: '${{ vars.GEMINI_MODEL }}' + google_api_key: '${{ secrets.GOOGLE_API_KEY }}' + use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' + use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' + extensions: | + [ + "https://github.com/CallumHYoung/security.git" + ] + settings: |- + { + "model": { + "maxSessionTurns": 100 + }, + "telemetry": { + "enabled": ${{ vars.GOOGLE_CLOUD_PROJECT != '' }}, + "target": "gcp" + }, + "mcpServers": { + "github": { + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "GITHUB_PERSONAL_ACCESS_TOKEN", + "ghcr.io/github/github-mcp-server" + ], + "includeTools": [ + "add_comment_to_pending_review", + "create_pending_pull_request_review", + "pull_request_read", + "submit_pending_pull_request_review" + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" + } + } + }, + "tools": { + "core": [ + "run_shell_command(cat)", + "run_shell_command(echo)", + "run_shell_command(grep)", + "run_shell_command(head)", + "run_shell_command(tail)" + ] + } + } + prompt: '/security:analyze-github-pr' \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1eae0cf..81340bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ +.env + dist/ -node_modules/ +node_modules/ \ No newline at end of file From 32ef14b8c799c1e31910b1df9a028a66d08c1389 Mon Sep 17 00:00:00 2001 From: callumhyoung Date: Mon, 13 Oct 2025 16:38:12 -0700 Subject: [PATCH 06/10] Update timeout & extension location --- .github/workflows/gemini-review.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gemini-review.yml b/.github/workflows/gemini-review.yml index ff46e5e..4f7a202 100644 --- a/.github/workflows/gemini-review.yml +++ b/.github/workflows/gemini-review.yml @@ -19,7 +19,7 @@ defaults: jobs: review: runs-on: 'ubuntu-latest' - timeout-minutes: 7 + timeout-minutes: 15 permissions: contents: 'read' id-token: 'write' @@ -129,7 +129,7 @@ jobs: use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' extensions: | [ - "https://github.com/CallumHYoung/security.git" + "https://github.com/gemini-cli-extensions/security.git" ] settings: |- { From 61e0f72e5797e3be6566f70d41a6aa3110c82c8c Mon Sep 17 00:00:00 2001 From: callumhyoung Date: Mon, 13 Oct 2025 16:40:28 -0700 Subject: [PATCH 07/10] Rename review workflow to include security analysis --- .github/workflows/gemini-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gemini-review.yml b/.github/workflows/gemini-review.yml index 4f7a202..9e340dd 100644 --- a/.github/workflows/gemini-review.yml +++ b/.github/workflows/gemini-review.yml @@ -1,4 +1,4 @@ -name: '🔎 Gemini Review' +name: '🔎 Gemini Review & Security Analysis' on: workflow_call: From dee9f8254a5bc56894aba78458dcf6d0d59ce546 Mon Sep 17 00:00:00 2001 From: callumhyoung Date: Tue, 14 Oct 2025 10:18:20 -0700 Subject: [PATCH 08/10] Update README.md and remove invoke workflow --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2458edf..b62edd3 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,13 @@ By default, the `/security:analyze` command determines the scope of the analysis ## GitHub Integration -Coming soon! +Integrating the Gemini CLI Security Extension into your GitHub worklfow for analyzing incoming code can be done with the following: + +* Base your workflow off of the [google-github-actions/run-gemini-cli GitHub Action](https://github.com/google-github-actions/run-gemini-cli) +* Ensure that the [gemini-cli-extensions/security](https://github.com/gemini-cli-extensions/security) extension is passed to the action for installation. +* Pass `/security:analyze-github-pr` as the prompt to invoke the Security Extension! + +See this [Example Workflow](https://github.com/gemini-cli-extensions/security/blob/main/.github/workflows/github-review.yml) ## Benchmark From e099caf7d748d27aa532232d55228daa1d6e54d3 Mon Sep 17 00:00:00 2001 From: callumhyoung Date: Tue, 14 Oct 2025 10:38:32 -0700 Subject: [PATCH 09/10] Simplify review workflow by removing dispatch --- .github/workflows/gemini-dispatch.yml | 185 -------------------------- .github/workflows/gemini-invoke.yml | 122 ----------------- .github/workflows/gemini-review.yml | 29 ++-- 3 files changed, 21 insertions(+), 315 deletions(-) delete mode 100644 .github/workflows/gemini-dispatch.yml delete mode 100644 .github/workflows/gemini-invoke.yml diff --git a/.github/workflows/gemini-dispatch.yml b/.github/workflows/gemini-dispatch.yml deleted file mode 100644 index 1cc5f42..0000000 --- a/.github/workflows/gemini-dispatch.yml +++ /dev/null @@ -1,185 +0,0 @@ -name: '🔀 Gemini Dispatch' - -on: - pull_request_review_comment: - types: - - 'created' - pull_request_review: - types: - - 'submitted' - pull_request: - types: - - 'opened' - issues: - types: - - 'opened' - - 'reopened' - issue_comment: - types: - - 'created' - -defaults: - run: - shell: 'bash' - -jobs: - debugger: - if: |- - ${{ fromJSON(vars.DEBUG || vars.ACTIONS_STEP_DEBUG || false) }} - runs-on: 'ubuntu-latest' - permissions: - contents: 'read' - steps: - - name: 'Print context for debugging' - env: - DEBUG_event_name: '${{ github.event_name }}' - DEBUG_event__action: '${{ github.event.action }}' - DEBUG_event__comment__author_association: '${{ github.event.comment.author_association }}' - DEBUG_event__issue__author_association: '${{ github.event.issue.author_association }}' - DEBUG_event__pull_request__author_association: '${{ github.event.pull_request.author_association }}' - DEBUG_event__review__author_association: '${{ github.event.review.author_association }}' - DEBUG_event: '${{ toJSON(github.event) }}' - run: |- - env | grep '^DEBUG_' - - dispatch: - # For PRs: only if not from a fork - # For comments: only if user types @gemini-cli and is OWNER/MEMBER/COLLABORATOR - # For issues: only on open/reopen - if: |- - ( - github.event_name == 'pull_request' && - github.event.pull_request.head.repo.fork == false - ) || ( - github.event.sender.type == 'User' && - startsWith(github.event.comment.body || github.event.review.body || github.event.issue.body, '@gemini-cli') && - contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association || github.event.review.author_association || github.event.issue.author_association) - ) || ( - github.event_name == 'issues' && - contains(fromJSON('["opened", "reopened"]'), github.event.action) - ) - runs-on: 'ubuntu-latest' - permissions: - contents: 'read' - issues: 'write' - pull-requests: 'write' - outputs: - command: '${{ steps.extract_command.outputs.command }}' - request: '${{ steps.extract_command.outputs.request }}' - additional_context: '${{ steps.extract_command.outputs.additional_context }}' - issue_number: '${{ github.event.pull_request.number || github.event.issue.number }}' - steps: - - name: 'Mint identity token' - id: 'mint_identity_token' - if: |- - ${{ vars.APP_ID }} - uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2 - with: - app-id: '${{ vars.APP_ID }}' - private-key: '${{ secrets.APP_PRIVATE_KEY }}' - permission-contents: 'read' - permission-issues: 'write' - permission-pull-requests: 'write' - - - name: 'Extract command' - id: 'extract_command' - uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea' # ratchet:actions/github-script@v7 - env: - EVENT_TYPE: '${{ github.event_name }}.${{ github.event.action }}' - REQUEST: '${{ github.event.comment.body || github.event.review.body || github.event.issue.body }}' - with: - script: | - const request = process.env.REQUEST; - const eventType = process.env.EVENT_TYPE - core.setOutput('request', request); - - if (request.startsWith("@gemini-cli /review")) { - core.setOutput('command', 'review'); - const additionalContext = request.replace(/^@gemini-cli \/review/, '').trim(); - core.setOutput('additional_context', additionalContext); - } else if (request.startsWith("@gemini-cli")) { - core.setOutput('command', 'invoke'); - const additionalContext = request.replace(/^@gemini-cli/, '').trim(); - core.setOutput('additional_context', additionalContext); - } else if (eventType === 'pull_request.opened') { - core.setOutput('command', 'review'); - } else { - core.setOutput('command', 'fallthrough'); - } - - - name: 'Acknowledge request' - env: - GITHUB_TOKEN: '${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}' - ISSUE_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' - MESSAGE: |- - 🤖 Hi @${{ github.actor }}, I've received your request, and I'm working on it now! You can track my progress [in the logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for more details. - REPOSITORY: '${{ github.repository }}' - run: |- - gh issue comment "${ISSUE_NUMBER}" \ - --body "${MESSAGE}" \ - --repo "${REPOSITORY}" - - review: - needs: 'dispatch' - if: |- - ${{ needs.dispatch.outputs.command == 'review' }} - uses: './.github/workflows/gemini-review.yml' - permissions: - contents: 'read' - id-token: 'write' - issues: 'write' - pull-requests: 'write' - with: - additional_context: '${{ needs.dispatch.outputs.additional_context }}' - secrets: 'inherit' - - invoke: - needs: 'dispatch' - if: |- - ${{ needs.dispatch.outputs.command == 'invoke' }} - uses: './.github/workflows/gemini-invoke.yml' - permissions: - contents: 'read' - id-token: 'write' - issues: 'write' - pull-requests: 'write' - with: - additional_context: '${{ needs.dispatch.outputs.additional_context }}' - secrets: 'inherit' - - fallthrough: - needs: - - 'dispatch' - - 'review' - - 'invoke' - if: |- - ${{ always() && !cancelled() && (failure() || needs.dispatch.outputs.command == 'fallthrough') }} - runs-on: 'ubuntu-latest' - permissions: - contents: 'read' - issues: 'write' - pull-requests: 'write' - steps: - - name: 'Mint identity token' - id: 'mint_identity_token' - if: |- - ${{ vars.APP_ID }} - uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2 - with: - app-id: '${{ vars.APP_ID }}' - private-key: '${{ secrets.APP_PRIVATE_KEY }}' - permission-contents: 'read' - permission-issues: 'write' - permission-pull-requests: 'write' - - - name: 'Send failure comment' - env: - GITHUB_TOKEN: '${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}' - ISSUE_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' - MESSAGE: |- - 🤖 I'm sorry @${{ github.actor }}, but I was unable to process your request. Please [see the logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for more details. - REPOSITORY: '${{ github.repository }}' - run: |- - gh issue comment "${ISSUE_NUMBER}" \ - --body "${MESSAGE}" \ - --repo "${REPOSITORY}" \ No newline at end of file diff --git a/.github/workflows/gemini-invoke.yml b/.github/workflows/gemini-invoke.yml deleted file mode 100644 index 8e06994..0000000 --- a/.github/workflows/gemini-invoke.yml +++ /dev/null @@ -1,122 +0,0 @@ -name: '▶️ Gemini Invoke' - -on: - workflow_call: - inputs: - additional_context: - type: 'string' - description: 'Any additional context from the request' - required: false - -concurrency: - group: '${{ github.workflow }}-invoke-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number }}' - cancel-in-progress: false - -defaults: - run: - shell: 'bash' - -jobs: - invoke: - runs-on: 'ubuntu-latest' - permissions: - contents: 'read' - id-token: 'write' - issues: 'write' - pull-requests: 'write' - steps: - - name: 'Mint identity token' - id: 'mint_identity_token' - if: |- - ${{ vars.APP_ID }} - uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2 - with: - app-id: '${{ vars.APP_ID }}' - private-key: '${{ secrets.APP_PRIVATE_KEY }}' - permission-contents: 'read' - permission-issues: 'write' - permission-pull-requests: 'write' - - - name: 'Run Gemini CLI' - id: 'run_gemini' - uses: 'google-github-actions/run-gemini-cli@main' # ratchet:exclude - env: - TITLE: '${{ github.event.pull_request.title || github.event.issue.title }}' - DESCRIPTION: '${{ github.event.pull_request.body || github.event.issue.body }}' - EVENT_NAME: '${{ github.event_name }}' - GITHUB_TOKEN: '${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}' - IS_PULL_REQUEST: '${{ !!github.event.pull_request }}' - ISSUE_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' - REPOSITORY: '${{ github.repository }}' - ADDITIONAL_CONTEXT: '${{ inputs.additional_context }}' - with: - gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}' - gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}' - gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}' - gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' - gemini_api_key: '${{ secrets.GEMINI_API_KEY }}' - gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}' - gemini_debug: '${{ fromJSON(vars.DEBUG || vars.ACTIONS_STEP_DEBUG || false) }}' - gemini_model: '${{ vars.GEMINI_MODEL }}' - google_api_key: '${{ secrets.GOOGLE_API_KEY }}' - use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' - use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' - settings: |- - { - "model": { - "maxSessionTurns": 25 - }, - "telemetry": { - "enabled": ${{ vars.GOOGLE_CLOUD_PROJECT != '' }}, - "target": "gcp" - }, - "mcpServers": { - "github": { - "command": "docker", - "args": [ - "run", - "-i", - "--rm", - "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN", - "ghcr.io/github/github-mcp-server" - ], - "includeTools": [ - "add_issue_comment", - "get_issue", - "get_issue_comments", - "list_issues", - "search_issues", - "create_pull_request", - "get_pull_request", - "get_pull_request_comments", - "get_pull_request_diff", - "get_pull_request_files", - "list_pull_requests", - "search_pull_requests", - "create_branch", - "create_or_update_file", - "delete_file", - "fork_repository", - "get_commit", - "get_file_contents", - "list_commits", - "push_files", - "search_code" - ], - "env": { - "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" - } - } - }, - "tools": { - "core": [ - "run_shell_command(cat)", - "run_shell_command(echo)", - "run_shell_command(grep)", - "run_shell_command(head)", - "run_shell_command(tail)" - ] - } - } - prompt: '/gemini-invoke' \ No newline at end of file diff --git a/.github/workflows/gemini-review.yml b/.github/workflows/gemini-review.yml index 9e340dd..edfe400 100644 --- a/.github/workflows/gemini-review.yml +++ b/.github/workflows/gemini-review.yml @@ -1,12 +1,12 @@ name: '🔎 Gemini Review & Security Analysis' on: - workflow_call: - inputs: - additional_context: - type: 'string' - description: 'Any additional context from the request' - required: false + pull_request: + types: + - 'opened' + issue_comment: + types: + - 'created' concurrency: group: '${{ github.workflow }}-review-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number }}' @@ -18,6 +18,9 @@ defaults: jobs: review: + if: | + (github.event_name == 'pull_request' && github.event.action == 'opened') || + (github.event_name == 'issue_comment' && github.event.comment.body == '@gemini-cli /review') runs-on: 'ubuntu-latest' timeout-minutes: 15 permissions: @@ -37,6 +40,18 @@ jobs: permission-contents: 'read' permission-issues: 'write' permission-pull-requests: 'write' + + - name: 'Acknowledge request' + env: + GITHUB_TOKEN: '${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}' + ISSUE_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' + MESSAGE: |- + 🤖 Hi @${{ github.actor }}, I've received your request, and I'm working on it now! You can track my progress [in the logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for more details. + REPOSITORY: '${{ github.repository }}' + run: |- + gh issue comment "${ISSUE_NUMBER}" \ + --body "${MESSAGE}" \ + --repo "${REPOSITORY}" - name: 'Checkout repository' uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 @@ -50,7 +65,6 @@ jobs: ISSUE_BODY: '${{ github.event.pull_request.body || github.event.issue.body }}' PULL_REQUEST_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' REPOSITORY: '${{ github.repository }}' - ADDITIONAL_CONTEXT: '${{ inputs.additional_context }}' with: gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}' gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}' @@ -114,7 +128,6 @@ jobs: ISSUE_BODY: '${{ github.event.pull_request.body || github.event.issue.body }}' PULL_REQUEST_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' REPOSITORY: '${{ github.repository }}' - ADDITIONAL_CONTEXT: '${{ inputs.additional_context }}' with: gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}' gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}' From 1a417bd506955da553f21fe4f64d5ccd4d7e6d95 Mon Sep 17 00:00:00 2001 From: callumhyoung Date: Tue, 14 Oct 2025 13:58:22 -0700 Subject: [PATCH 10/10] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b62edd3..6672a52 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ By default, the `/security:analyze` command determines the scope of the analysis ## GitHub Integration -Integrating the Gemini CLI Security Extension into your GitHub worklfow for analyzing incoming code can be done with the following: +Integrate the Gemini CLI Security Extension into your GitHub worklfow for analyzing incoming code can be done with the following: * Base your workflow off of the [google-github-actions/run-gemini-cli GitHub Action](https://github.com/google-github-actions/run-gemini-cli) * Ensure that the [gemini-cli-extensions/security](https://github.com/gemini-cli-extensions/security) extension is passed to the action for installation.