diff --git a/.github/workflows/gemini-cli.yml b/.github/workflows/gemini-cli.yml index 23179d0f..5fbd2a84 100644 --- a/.github/workflows/gemini-cli.yml +++ b/.github/workflows/gemini-cli.yml @@ -28,7 +28,9 @@ permissions: jobs: gemini-cli: - # This condition is complex to ensure we only run when explicitly invoked. + # This condition seeks to ensure the action is only run when it is triggered by a trusted user. + # For private repos, users who have access to the repo are considered trusted. + # For public repos, users who members, owners, or collaborators are considered trusted. if: |- github.event_name == 'workflow_dispatch' || ( @@ -36,7 +38,10 @@ jobs: contains(github.event.issue.body, '@gemini-cli') && !contains(github.event.issue.body, '@gemini-cli /review') && !contains(github.event.issue.body, '@gemini-cli /triage') && - contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.issue.author_association) + ( + github.event.repository.private == true || + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.issue.author_association) + ) ) || ( ( @@ -46,18 +51,23 @@ jobs: contains(github.event.comment.body, '@gemini-cli') && !contains(github.event.comment.body, '@gemini-cli /review') && !contains(github.event.comment.body, '@gemini-cli /triage') && - contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) + ( + github.event.repository.private == true || + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) + ) ) || ( github.event_name == 'pull_request_review' && contains(github.event.review.body, '@gemini-cli') && !contains(github.event.review.body, '@gemini-cli /review') && !contains(github.event.review.body, '@gemini-cli /triage') && - contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association) + ( + github.event.repository.private == true || + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association) + ) ) timeout-minutes: 10 runs-on: 'ubuntu-latest' - steps: - name: 'Generate GitHub App Token' id: 'generate_token' @@ -205,6 +215,7 @@ jobs: use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' settings: |- { + "debug": ${{ fromJSON(env.DEBUG || env.ACTIONS_STEP_DEBUG || false) }}, "maxSessionTurns": 50, "telemetry": { "enabled": true, diff --git a/.github/workflows/gemini-issue-automated-triage.yml b/.github/workflows/gemini-issue-automated-triage.yml index 4652f53f..b0f8060c 100644 --- a/.github/workflows/gemini-issue-automated-triage.yml +++ b/.github/workflows/gemini-issue-automated-triage.yml @@ -41,7 +41,6 @@ jobs: ) timeout-minutes: 5 runs-on: 'ubuntu-latest' - steps: - name: 'Checkout repository' uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 @@ -55,15 +54,31 @@ jobs: app-id: '${{ vars.APP_ID }}' private-key: '${{ secrets.APP_PRIVATE_KEY }}' - - name: 'Run Gemini Issue Triage' + - 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 labelNames = labels.map(label => label.name); + core.setOutput('available_labels', labelNames.join(',')); + core.info(`Found ${labelNames.length} labels: ${labelNames.join(', ')}`); + return labelNames; + + - name: 'Run Gemini Issue Analysis' uses: './' - id: 'gemini_issue_triage' + id: 'gemini_issue_analysis' env: - GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + GITHUB_TOKEN: '' # Do not pass any auth token here since this runs on untrusted inputs ISSUE_TITLE: '${{ github.event.issue.title }}' ISSUE_BODY: '${{ github.event.issue.body }}' ISSUE_NUMBER: '${{ github.event.issue.number }}' REPOSITORY: '${{ github.repository }}' + AVAILABLE_LABELS: '${{ steps.get_labels.outputs.available_labels }}' with: gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}' gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' @@ -75,11 +90,10 @@ jobs: use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' settings: |- { + "debug": ${{ fromJSON(env.DEBUG || env.ACTIONS_STEP_DEBUG || false) }}, "maxSessionTurns": 25, "coreTools": [ - "run_shell_command(echo)", - "run_shell_command(gh label list)", - "run_shell_command(gh issue edit)" + "run_shell_command(echo)" ], "telemetry": { "enabled": true, @@ -90,41 +104,88 @@ jobs: ## Role You are an issue triage assistant. Analyze the current GitHub issue - and apply the most appropriate existing labels. Use the available + and identify the most appropriate existing labels. Use the available tools to gather information; do not ask for information to be provided. ## Steps - 1. Run: `gh label list` to get all available labels. + 1. Review the available labels in the environment variable: "${AVAILABLE_LABELS}". 2. Review the issue title and body provided in the environment variables: "${ISSUE_TITLE}" and "${ISSUE_BODY}". - 3. Classify issues by their kind (bug, enhancement, documentation, - cleanup, etc) and their priority (p0, p1, p2, p3). Set the - labels accoridng to the format `kind/*` and `priority/*` patterns. - 4. Apply the selected labels to this issue using: - `gh issue edit "${ISSUE_NUMBER}" --add-label "label1,label2"` - 5. If the "status/needs-triage" label is present, remove it using: - `gh issue edit "${ISSUE_NUMBER}" --remove-label "status/needs-triage"` + 3. Classify the issue by the appropriate labels from the available labels. + 4. Output the appropriate labels for this issue in JSON format with explanation, for example: + ``` + {"labels_to_set": ["kind/bug", "priority/p0"], "explanation": "This is a critical bug report affecting main functionality"} + ``` + 5. If the issue cannot be classified using the available labels, output: + ``` + {"labels_to_set": [], "explanation": "Unable to classify this issue with available labels"} + ``` ## Guidelines - Only use labels that already exist in the repository - - Do not add comments or modify the issue content - - Triage only the current issue - Assign all applicable labels based on the issue content - Reference all shell variables as "${VAR}" (with quotes and braces) + - Output only valid JSON format + - Do not include any explanation or additional text, just the JSON - - name: 'Post Issue Triage Failure Comment' + - name: 'Apply Labels to Issue' if: |- - ${{ failure() && steps.gemini_issue_triage.outcome == 'failure' }} + ${{ steps.gemini_issue_analysis.outputs.summary != '' }} + env: + REPOSITORY: '${{ github.repository }}' + ISSUE_NUMBER: '${{ github.event.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: |- + // Strip code block markers if present + const rawLabels = process.env.LABELS_OUTPUT; + core.info(`Raw labels JSON: ${rawLabels}`); + let parsedLabels; + try { + const trimmedLabels = rawLabels.replace(/^```(?:json)?\s*/, '').replace(/\s*```$/, '').trim(); + parsedLabels = JSON.parse(trimmedLabels); + core.info(`Parsed labels JSON: ${JSON.stringify(parsedLabels)}`); + } catch (err) { + core.setFailed(`Failed to parse labels JSON from Gemini output: ${err.message}\nRaw output: ${rawLabels}`); + return; + } + + const issueNumber = parseInt(process.env.ISSUE_NUMBER); + + // Set labels based on triage result + if (parsedLabels.labels_to_set && parsedLabels.labels_to_set.length > 0) { + await github.rest.issues.setLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + labels: parsedLabels.labels_to_set + }); + const explanation = parsedLabels.explanation ? ` - ${parsedLabels.explanation}` : ''; + core.info(`Successfully set labels for #${issueNumber}: ${parsedLabels.labels_to_set.join(', ')}${explanation}`); + } else { + // If no labels to set, leave the issue as is + const explanation = parsedLabels.explanation ? ` - ${parsedLabels.explanation}` : ''; + core.info(`No labels to set for #${issueNumber}, leaving as is${explanation}`); + } + + - name: 'Post Issue Analysis Failure Comment' + if: |- + ${{ failure() && steps.gemini_issue_analysis.outcome == 'failure' }} + env: + ISSUE_NUMBER: '${{ github.event.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: '${{ github.repository }}'.split('/')[0], - repo: '${{ github.repository }}'.split('/')[1], - issue_number: '${{ github.event.issue.number }}', - 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.' + 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.' }) diff --git a/.github/workflows/gemini-issue-scheduled-triage.yml b/.github/workflows/gemini-issue-scheduled-triage.yml index 5cb44ffd..83877724 100644 --- a/.github/workflows/gemini-issue-scheduled-triage.yml +++ b/.github/workflows/gemini-issue-scheduled-triage.yml @@ -23,7 +23,6 @@ jobs: triage-issues: timeout-minutes: 5 runs-on: 'ubuntu-latest' - steps: - name: 'Checkout repository' uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 @@ -63,15 +62,31 @@ jobs: ISSUE_COUNT="$(echo "${ISSUES}" | jq 'length')" echo "✅ Found ${ISSUE_COUNT} issues to triage! 🎯" - - name: 'Run Gemini Issue Triage' + - 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 labelNames = labels.map(label => label.name); + core.setOutput('available_labels', labelNames.join(',')); + core.info(`Found ${labelNames.length} labels: ${labelNames.join(', ')}`); + return labelNames; + + - name: 'Run Gemini Issue Analysis' if: |- ${{ steps.find_issues.outputs.issues_to_triage != '[]' }} uses: './' - id: 'gemini_issue_triage' + id: 'gemini_issue_analysis' env: - GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + GITHUB_TOKEN: '' # Do not pass any auth token here since this runs on untrusted inputs ISSUES_TO_TRIAGE: '${{ steps.find_issues.outputs.issues_to_triage }}' REPOSITORY: '${{ github.repository }}' + AVAILABLE_LABELS: '${{ steps.get_labels.outputs.available_labels }}' with: gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}' gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' @@ -83,12 +98,10 @@ jobs: use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' settings: |- { + "debug": ${{ fromJSON(env.DEBUG || env.ACTIONS_STEP_DEBUG || false) }}, "maxSessionTurns": 25, "coreTools": [ - "run_shell_command(echo)", - "run_shell_command(gh label list)", - "run_shell_command(gh issue edit)", - "run_shell_command(gh issue list)" + "run_shell_command(echo)" ], "telemetry": { "enabled": true, @@ -98,26 +111,83 @@ jobs: prompt: |- ## Role - You are an issue triage assistant. Analyze issues and apply - appropriate labels. Use the available tools to gather information; - do not ask for information to be provided. + You are an issue triage assistant. Analyze the GitHub issues and + identify the most appropriate existing labels to apply. ## Steps - 1. Run: `gh label list` - 2. Check environment variable: "${ISSUES_TO_TRIAGE}" (JSON array - of issues) - 3. For each issue, apply labels: - `gh issue edit "${ISSUE_NUMBER}" --add-label "label1,label2"`. - If available, set labels that follow the `kind/*`, `area/*`, - and `priority/*` patterns. - 4. For each issue, if the `status/needs-triage` label is present, - remove it using: - `gh issue edit "${ISSUE_NUMBER}" --remove-label "status/needs-triage"` + 1. Review the available labels in the environment variable: "${AVAILABLE_LABELS}". + 2. Review the issues in the environment variable: "${ISSUES_TO_TRIAGE}". + 3. For each issue, classify it by the appropriate labels from the available labels. + 4. Output a JSON array of objects, each containing the issue number, + the labels to set, and a brief explanation. For example: + ``` + [ + { + "issue_number": 123, + "labels_to_set": ["kind/bug", "priority/p2"], + "explanation": "This is a bug report with high priority based on the error description" + }, + { + "issue_number": 456, + "labels_to_set": ["kind/enhancement"], + "explanation": "This is a feature request for improving the UI" + } + ] + ``` + 5. If an issue cannot be classified, do not include it in the output array. ## Guidelines - - Only use existing repository labels - - Do not add comments - - Triage each issue independently + - Only use labels that already exist in the repository + - Assign all applicable labels based on the issue content - Reference all shell variables as "${VAR}" (with quotes and braces) + - Output only valid JSON format + - Do not include any explanation or additional text, just the JSON + + - name: 'Apply Labels to Issues' + if: |- + ${{ steps.gemini_issue_analysis.outcome == 'success' && + steps.gemini_issue_analysis.outputs.summary != '[]' }} + env: + REPOSITORY: '${{ github.repository }}' + 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: |- + // Strip code block markers if present + const rawLabels = process.env.LABELS_OUTPUT; + core.info(`Raw labels JSON: ${rawLabels}`); + let parsedLabels; + try { + const trimmedLabels = rawLabels.replace(/^```(?:json)?\s*/, '').replace(/\s*```$/, '').trim(); + parsedLabels = JSON.parse(trimmedLabels); + core.info(`Parsed labels JSON: ${JSON.stringify(parsedLabels)}`); + } catch (err) { + core.setFailed(`Failed to parse labels JSON from Gemini output: ${err.message}\nRaw output: ${rawLabels}`); + return; + } + + for (const entry of parsedLabels) { + const issueNumber = entry.issue_number; + if (!issueNumber) { + core.info(`Skipping entry with no issue number: ${JSON.stringify(entry)}`); + continue; + } + + // Set labels based on triage result + if (entry.labels_to_set && entry.labels_to_set.length > 0) { + await github.rest.issues.setLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + labels: entry.labels_to_set + }); + const explanation = entry.explanation ? ` - ${entry.explanation}` : ''; + core.info(`Successfully set labels for #${issueNumber}: ${entry.labels_to_set.join(', ')}${explanation}`); + } else { + // If no labels to set, leave the issue as is + core.info(`No labels to set for #${issueNumber}, leaving as is`); + } + } diff --git a/.github/workflows/gemini-pr-review.yml b/.github/workflows/gemini-pr-review.yml index 0d3ab42a..254ebb22 100644 --- a/.github/workflows/gemini-pr-review.yml +++ b/.github/workflows/gemini-pr-review.yml @@ -38,11 +38,17 @@ permissions: jobs: review-pr: + # This condition seeks to ensure the action is only run when it is triggered by a trusted user. + # For private repos, users who have access to the repo are considered trusted. + # For public repos, users who members, owners, or collaborators are considered trusted. if: |- github.event_name == 'workflow_dispatch' || ( github.event_name == 'pull_request' && - contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association) + ( + github.event.repository.private == true || + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association) + ) ) || ( ( @@ -53,16 +59,21 @@ jobs: github.event_name == 'pull_request_review_comment' ) && contains(github.event.comment.body, '@gemini-cli /review') && - contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) + ( + github.event.repository.private == true || + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) + ) ) || ( github.event_name == 'pull_request_review' && contains(github.event.review.body, '@gemini-cli /review') && - contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association) + ( + github.event.repository.private == true || + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association) + ) ) timeout-minutes: 5 runs-on: 'ubuntu-latest' - steps: - name: 'Checkout PR code' uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 @@ -109,14 +120,14 @@ jobs: } >> "${GITHUB_OUTPUT}" - - name: 'Get PR details (issue_comment)' + - name: 'Get PR details (issue_comment & reviews)' id: 'get_pr_comment' if: |- - ${{ github.event_name == 'issue_comment' }} + ${{ github.event_name == 'issue_comment' || github.event_name == 'pull_request_review' || github.event_name == 'pull_request_review_comment' }} env: GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' - COMMENT_BODY: '${{ github.event.comment.body }}' - PR_NUMBER: '${{ github.event.issue.number }}' + COMMENT_BODY: '${{ github.event.comment.body || github.event.review.body }}' + PR_NUMBER: '${{ github.event.issue.number || github.event.pull_request.number }}' run: |- set -euo pipefail @@ -161,6 +172,7 @@ jobs: use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' settings: |- { + "debug": ${{ fromJSON(env.DEBUG || env.ACTIONS_STEP_DEBUG || false) }}, "maxSessionTurns": 20, "mcpServers": { "github": { diff --git a/README.md b/README.md index 868bc94f..6f401ab8 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,10 @@ This action can be used to automatically review pull requests when they are opened. For a detailed guide on how to set up the pull request review system, go to the [GitHub PR Review workflow documentation](./examples/workflows/pr-review). +There is a [known issue](https://github.com/google-github-actions/run-gemini-cli/issues/169) that action bot may approve the PR occasionally, +to avoid this situation as org owner you can restrict who can approve the PR following +[Code Review Limits](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/managing-repository-settings/managing-pull-request-reviews-in-your-repository#enabling-code-review-limits). + ### Gemini CLI Assistant This type of action can be used to invoke a general-purpose, conversational Gemini diff --git a/action.yml b/action.yml index 1550f2b4..79562d45 100644 --- a/action.yml +++ b/action.yml @@ -118,7 +118,7 @@ runs: if [[ "${VERSION_INPUT}" == "latest" || "${VERSION_INPUT}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9\.-]+)?(\+[a-zA-Z0-9\.-]+)?$ ]]; then echo "Installing Gemini CLI from npm: @google/gemini-cli@${VERSION_INPUT}" - npm install -g @google/gemini-cli@"${VERSION_INPUT}" + npm install --silent --no-audit --prefer-offline --global @google/gemini-cli@"${VERSION_INPUT}" else echo "Installing Gemini CLI from GitHub: github:google-gemini/gemini-cli#${VERSION_INPUT}" git clone https://github.com/google-gemini/gemini-cli.git @@ -126,7 +126,7 @@ runs: git checkout "${VERSION_INPUT}" npm install npm run bundle - npm install -g . + npm install --silent --no-audit --prefer-offline --global . fi echo "Verifying installation:" if command -v gemini >/dev/null 2>&1; then @@ -138,23 +138,43 @@ runs: - name: 'Run Gemini CLI' id: 'gemini_run' + shell: 'bash' run: |- - set -e + set -euo pipefail # Unset GEMINI_API_KEY if empty if [ -z "${GEMINI_API_KEY}" ]; then unset GEMINI_API_KEY fi + # Create a temporary directory for storing the output, and ensure it's + # cleaned up later + TEMP_OUTPUT="$(mktemp -p "${RUNNER_TEMP}" gemini.XXXXXXXXXX)" + function cleanup { + rm -f "${TEMP_OUTPUT}" + } + trap cleanup EXIT + # Run Gemini CLI with the provided prompt - GEMINI_RESPONSE=$(gemini --yolo --prompt "${PROMPT}") + if ! gemini --yolo --prompt "${PROMPT}" &> "${TEMP_OUTPUT}"; then + GEMINI_RESPONSE="$(cat "${TEMP_OUTPUT}")" + FIRST_LINE="$(echo "${GEMINI_RESPONSE}" | head -n1)" + echo "::error title=Gemini CLI execution failed::${FIRST_LINE}" + echo "${GEMINI_RESPONSE}" + exit 1 + fi + + GEMINI_RESPONSE="$(cat "${TEMP_OUTPUT}")" + + # Print the response + echo "::group::Gemini response" + echo "${GEMINI_RESPONSE}" + echo "::endgroup::" # Set the captured response as a step output, supporting multiline echo "gemini_response<> "${GITHUB_OUTPUT}" echo "${GEMINI_RESPONSE}" >> "${GITHUB_OUTPUT}" echo "EOF" >> "${GITHUB_OUTPUT}" - echo "${GEMINI_RESPONSE}" - shell: 'bash' env: GEMINI_API_KEY: '${{ inputs.gemini_api_key }}' SURFACE: 'GitHub' diff --git a/examples/workflows/AWESOME.md b/examples/workflows/AWESOME.md index 175cea27..e9fe87fe 100644 --- a/examples/workflows/AWESOME.md +++ b/examples/workflows/AWESOME.md @@ -31,7 +31,38 @@ Workflows that help maintain code quality, perform analysis, or enforce standard Workflows that help manage GitHub issues, projects, or team collaboration. -*No workflows yet. Be the first to contribute!* +### 1. Workflow to Enforce Contribution Guidelines in Pull Requests + +**Repository:** [jasmeetsb/gemini-github-actions](https://github.com/jasmeetsb/gemini-github-actions) + +**Description:** Automates validation of pull requests against your repository's CONTRIBUTING.md using the Google Gemini CLI. The workflow posts a single upserted PR comment indicating PASS/FAIL with a concise checklist of actionable items, and can optionally fail the job to enforce compliance. + +**Key Features:** + +- Reads and evaluates PR title, body, and diff against CONTRIBUTING.md +- Posts a single PR comment with a visible PASS/FAIL marker in Comment Title and details of compliance status in the comment body +- Optional enforcement: fail the workflow when violations are detected + +**Setup Requirements:** + +- Copy [.github/workflows/pr-contribution-guidelines-enforcement.yml](https://github.com/jasmeetsb/gemini-github-actions/blob/main/.github/workflows/pr-contribution-guidelines-enforcement.yml) to your .github/workflows/ folder. +- File: `CONTRIBUTING.md` at the repository root +- (Optional) Repository variable `FAIL_ON_GUIDELINE_VIOLATIONS=true` to fail the workflow on violations + +**Example Usage:** + +- Define contribution guidelines in CONTRIBUTING.md file +- Open a new PR or update an existing PR, which would then trigger the workflow +- Workflow will validate the PR against the contribution guidelines and add a comment in the PR with PASS/FAIL status and details of guideline compliance and non-compliance + + **OR** + +- Add following comment in an existing PR **"/validate-contribution"** to trigger the workflow + +**Workflow File:** + +- Example location in this repo: [.github/workflows/pr-contribution-guidelines-enforcement.yml](https://github.com/jasmeetsb/gemini-github-actions/blob/main/.github/workflows/pr-contribution-guidelines-enforcement.yml) +- Typical usage in a consumer repo: `.github/workflows/pr-contribution-guidelines-enforcement.yml` (copy the file and adjust settings/secrets as needed) ### 📝 Documentation diff --git a/examples/workflows/CONFIGURATION.md b/examples/workflows/CONFIGURATION.md index 3c6d7503..55108ffd 100644 --- a/examples/workflows/CONFIGURATION.md +++ b/examples/workflows/CONFIGURATION.md @@ -6,6 +6,8 @@ This guide covers how to customize and configure Gemini CLI workflows to meet yo - [How to Configure Gemini CLI](#how-to-configure-gemini-cli) - [Key Settings](#key-settings) - [Conversation Length (`maxSessionTurns`)](#conversation-length-maxsessionturns) + - [Allowlist Tools (`coreTools`)](#allowlist-tools-coretools) + - [MCP Servers (`mcpServers`)](#mcp-servers-mcpservers) - [Custom Context and Guidance (`GEMINI.md`)](#custom-context-and-guidance-geminimd) - [GitHub Actions Workflow Settings](#github-actions-workflow-settings) - [Setting Timeouts](#setting-timeouts) @@ -43,6 +45,59 @@ with: } ``` +#### Allowlist Tools (`coreTools`) + +Allows you to specify a list of [built-in tools] that should be made available to the model. You can also use this to allowlist commands for shell tool. + +**Default:** All tools available for use by Gemini CLI. + +**How to configure:** + +Add the following to your workflow YAML file to specify core tools: + +```yaml +with: + settings: |- + { + "coreTools": [ + "read_file" + "run_shell_command(echo)", + "run_shell_command(gh label list)" + ] + } +``` + +#### MCP Servers (`mcpServers`) + +Configures connections to one or more Model Context Protocol (MCP) servers for discovering and using custom tools. This allows you to extend Gemini CLI GitHub Action with additional capabilities. + +**Default:** Empty + +**Example:** + +```yaml +with: + settings: |- + { + "mcpServers": { + "github": { + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "GITHUB_PERSONAL_ACCESS_TOKEN", + "ghcr.io/github/github-mcp-server" + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" + } + } + } + } +``` + ### Custom Context and Guidance (`GEMINI.md`) To provide Gemini CLI with custom instructions—such as coding conventions, architectural patterns, or other guidance—add a `GEMINI.md` file to the root of your repository. Gemini CLI will use the content of this file to inform its responses. @@ -60,3 +115,5 @@ Only users with the following roles can trigger the workflow: - Repository Owner (`OWNER`) - Repository Member (`MEMBER`) - Repository Collaborator (`COLLABORATOR`) + +[built-in tools]: https://github.com/google-gemini/gemini-cli/blob/main/docs/core/tools-api.md#built-in-tools diff --git a/examples/workflows/gemini-cli/gemini-cli.yml b/examples/workflows/gemini-cli/gemini-cli.yml index 3fbaedc7..41cf37c4 100644 --- a/examples/workflows/gemini-cli/gemini-cli.yml +++ b/examples/workflows/gemini-cli/gemini-cli.yml @@ -28,7 +28,9 @@ permissions: jobs: gemini-cli: - # This condition is complex to ensure we only run when explicitly invoked. + # This condition seeks to ensure the action is only run when it is triggered by a trusted user. + # For private repos, users who have access to the repo are considered trusted. + # For public repos, users who members, owners, or collaborators are considered trusted. if: |- github.event_name == 'workflow_dispatch' || ( @@ -36,7 +38,10 @@ jobs: contains(github.event.issue.body, '@gemini-cli') && !contains(github.event.issue.body, '@gemini-cli /review') && !contains(github.event.issue.body, '@gemini-cli /triage') && - contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.issue.author_association) + ( + github.event.repository.private == true || + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.issue.author_association) + ) ) || ( ( @@ -46,18 +51,23 @@ jobs: contains(github.event.comment.body, '@gemini-cli') && !contains(github.event.comment.body, '@gemini-cli /review') && !contains(github.event.comment.body, '@gemini-cli /triage') && - contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) + ( + github.event.repository.private == true || + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) + ) ) || ( github.event_name == 'pull_request_review' && contains(github.event.review.body, '@gemini-cli') && !contains(github.event.review.body, '@gemini-cli /review') && !contains(github.event.review.body, '@gemini-cli /triage') && - contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association) + ( + github.event.repository.private == true || + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association) + ) ) timeout-minutes: 10 runs-on: 'ubuntu-latest' - steps: - name: 'Generate GitHub App Token' id: 'generate_token' @@ -205,6 +215,7 @@ jobs: use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' settings: |- { + "debug": ${{ fromJSON(env.DEBUG || env.ACTIONS_STEP_DEBUG || false) }}, "maxSessionTurns": 50, "telemetry": { "enabled": false, diff --git a/examples/workflows/issue-triage/gemini-issue-automated-triage.yml b/examples/workflows/issue-triage/gemini-issue-automated-triage.yml index 50a67101..375bc0ed 100644 --- a/examples/workflows/issue-triage/gemini-issue-automated-triage.yml +++ b/examples/workflows/issue-triage/gemini-issue-automated-triage.yml @@ -41,7 +41,6 @@ jobs: ) timeout-minutes: 5 runs-on: 'ubuntu-latest' - steps: - name: 'Checkout repository' uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 @@ -55,15 +54,31 @@ jobs: app-id: '${{ vars.APP_ID }}' private-key: '${{ secrets.APP_PRIVATE_KEY }}' - - name: 'Run Gemini Issue Triage' + - 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 labelNames = labels.map(label => label.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@v0' - id: 'gemini_issue_triage' + id: 'gemini_issue_analysis' env: - GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + GITHUB_TOKEN: '' # Do not pass any auth token here since this runs on untrusted inputs ISSUE_TITLE: '${{ github.event.issue.title }}' ISSUE_BODY: '${{ github.event.issue.body }}' ISSUE_NUMBER: '${{ github.event.issue.number }}' REPOSITORY: '${{ github.repository }}' + AVAILABLE_LABELS: '${{ steps.get_labels.outputs.available_labels }}' with: gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}' gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' @@ -75,11 +90,10 @@ jobs: use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' settings: |- { + "debug": ${{ fromJSON(env.DEBUG || env.ACTIONS_STEP_DEBUG || false) }}, "maxSessionTurns": 25, "coreTools": [ - "run_shell_command(echo)", - "run_shell_command(gh label list)", - "run_shell_command(gh issue edit)" + "run_shell_command(echo)" ], "telemetry": { "enabled": false, @@ -90,41 +104,88 @@ jobs: ## Role You are an issue triage assistant. Analyze the current GitHub issue - and apply the most appropriate existing labels. Use the available + and identify the most appropriate existing labels. Use the available tools to gather information; do not ask for information to be provided. ## Steps - 1. Run: `gh label list` to get all available labels. + 1. Review the available labels in the environment variable: "${AVAILABLE_LABELS}". 2. Review the issue title and body provided in the environment variables: "${ISSUE_TITLE}" and "${ISSUE_BODY}". - 3. Classify issues by their kind (bug, enhancement, documentation, - cleanup, etc) and their priority (p0, p1, p2, p3). Set the - labels accoridng to the format `kind/*` and `priority/*` patterns. - 4. Apply the selected labels to this issue using: - `gh issue edit "${ISSUE_NUMBER}" --add-label "label1,label2"` - 5. If the "status/needs-triage" label is present, remove it using: - `gh issue edit "${ISSUE_NUMBER}" --remove-label "status/needs-triage"` + 3. Classify the issue by the appropriate labels from the available labels. + 4. Output the appropriate labels for this issue in JSON format with explanation, for example: + ``` + {"labels_to_set": ["kind/bug", "priority/p0"], "explanation": "This is a critical bug report affecting main functionality"} + ``` + 5. If the issue cannot be classified using the available labels, output: + ``` + {"labels_to_set": [], "explanation": "Unable to classify this issue with available labels"} + ``` ## Guidelines - Only use labels that already exist in the repository - - Do not add comments or modify the issue content - - Triage only the current issue - Assign all applicable labels based on the issue content - Reference all shell variables as "${VAR}" (with quotes and braces) + - Output only valid JSON format + - Do not include any explanation or additional text, just the JSON - - name: 'Post Issue Triage Failure Comment' + - name: 'Apply Labels to Issue' if: |- - ${{ failure() && steps.gemini_issue_triage.outcome == 'failure' }} + ${{ steps.gemini_issue_analysis.outputs.summary != '' }} + env: + REPOSITORY: '${{ github.repository }}' + ISSUE_NUMBER: '${{ github.event.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: |- + // Strip code block markers if present + const rawLabels = process.env.LABELS_OUTPUT; + core.info(`Raw labels JSON: ${rawLabels}`); + let parsedLabels; + try { + const trimmedLabels = rawLabels.replace(/^```(?:json)?\s*/, '').replace(/\s*```$/, '').trim(); + parsedLabels = JSON.parse(trimmedLabels); + core.info(`Parsed labels JSON: ${JSON.stringify(parsedLabels)}`); + } catch (err) { + core.setFailed(`Failed to parse labels JSON from Gemini output: ${err.message}\nRaw output: ${rawLabels}`); + return; + } + + const issueNumber = parseInt(process.env.ISSUE_NUMBER); + + // Set labels based on triage result + if (parsedLabels.labels_to_set && parsedLabels.labels_to_set.length > 0) { + await github.rest.issues.setLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + labels: parsedLabels.labels_to_set + }); + const explanation = parsedLabels.explanation ? ` - ${parsedLabels.explanation}` : ''; + core.info(`Successfully set labels for #${issueNumber}: ${parsedLabels.labels_to_set.join(', ')}${explanation}`); + } else { + // If no labels to set, leave the issue as is + const explanation = parsedLabels.explanation ? ` - ${parsedLabels.explanation}` : ''; + core.info(`No labels to set for #${issueNumber}, leaving as is${explanation}`); + } + + - name: 'Post Issue Analysis Failure Comment' + if: |- + ${{ failure() && steps.gemini_issue_analysis.outcome == 'failure' }} + env: + ISSUE_NUMBER: '${{ github.event.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: '${{ github.repository }}'.split('/')[0], - repo: '${{ github.repository }}'.split('/')[1], - issue_number: '${{ github.event.issue.number }}', - 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.' + 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.' }) diff --git a/examples/workflows/issue-triage/gemini-issue-scheduled-triage.yml b/examples/workflows/issue-triage/gemini-issue-scheduled-triage.yml index f4420597..878dc72c 100644 --- a/examples/workflows/issue-triage/gemini-issue-scheduled-triage.yml +++ b/examples/workflows/issue-triage/gemini-issue-scheduled-triage.yml @@ -23,7 +23,6 @@ jobs: triage-issues: timeout-minutes: 5 runs-on: 'ubuntu-latest' - steps: - name: 'Checkout repository' uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 @@ -63,15 +62,31 @@ jobs: ISSUE_COUNT="$(echo "${ISSUES}" | jq 'length')" echo "✅ Found ${ISSUE_COUNT} issues to triage! 🎯" - - name: 'Run Gemini Issue Triage' + - 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 labelNames = labels.map(label => label.name); + core.setOutput('available_labels', labelNames.join(',')); + core.info(`Found ${labelNames.length} labels: ${labelNames.join(', ')}`); + return labelNames; + + - name: 'Run Gemini Issue Analysis' if: |- ${{ steps.find_issues.outputs.issues_to_triage != '[]' }} uses: 'google-github-actions/run-gemini-cli@v0' - id: 'gemini_issue_triage' + id: 'gemini_issue_analysis' env: - GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' + GITHUB_TOKEN: '' # Do not pass any auth token here since this runs on untrusted inputs ISSUES_TO_TRIAGE: '${{ steps.find_issues.outputs.issues_to_triage }}' REPOSITORY: '${{ github.repository }}' + AVAILABLE_LABELS: '${{ steps.get_labels.outputs.available_labels }}' with: gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}' gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' @@ -83,12 +98,10 @@ jobs: use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' settings: |- { + "debug": ${{ fromJSON(env.DEBUG || env.ACTIONS_STEP_DEBUG || false) }}, "maxSessionTurns": 25, "coreTools": [ - "run_shell_command(echo)", - "run_shell_command(gh label list)", - "run_shell_command(gh issue edit)", - "run_shell_command(gh issue list)" + "run_shell_command(echo)" ], "telemetry": { "enabled": false, @@ -98,26 +111,83 @@ jobs: prompt: |- ## Role - You are an issue triage assistant. Analyze issues and apply - appropriate labels. Use the available tools to gather information; - do not ask for information to be provided. + You are an issue triage assistant. Analyze the GitHub issues and + identify the most appropriate existing labels to apply. ## Steps - 1. Run: `gh label list` - 2. Check environment variable: "${ISSUES_TO_TRIAGE}" (JSON array - of issues) - 3. For each issue, apply labels: - `gh issue edit "${ISSUE_NUMBER}" --add-label "label1,label2"`. - If available, set labels that follow the `kind/*`, `area/*`, - and `priority/*` patterns. - 4. For each issue, if the `status/needs-triage` label is present, - remove it using: - `gh issue edit "${ISSUE_NUMBER}" --remove-label "status/needs-triage"` + 1. Review the available labels in the environment variable: "${AVAILABLE_LABELS}". + 2. Review the issues in the environment variable: "${ISSUES_TO_TRIAGE}". + 3. For each issue, classify it by the appropriate labels from the available labels. + 4. Output a JSON array of objects, each containing the issue number, + the labels to set, and a brief explanation. For example: + ``` + [ + { + "issue_number": 123, + "labels_to_set": ["kind/bug", "priority/p2"], + "explanation": "This is a bug report with high priority based on the error description" + }, + { + "issue_number": 456, + "labels_to_set": ["kind/enhancement"], + "explanation": "This is a feature request for improving the UI" + } + ] + ``` + 5. If an issue cannot be classified, do not include it in the output array. ## Guidelines - - Only use existing repository labels - - Do not add comments - - Triage each issue independently + - Only use labels that already exist in the repository + - Assign all applicable labels based on the issue content - Reference all shell variables as "${VAR}" (with quotes and braces) + - Output only valid JSON format + - Do not include any explanation or additional text, just the JSON + + - name: 'Apply Labels to Issues' + if: |- + ${{ steps.gemini_issue_analysis.outcome == 'success' && + steps.gemini_issue_analysis.outputs.summary != '[]' }} + env: + REPOSITORY: '${{ github.repository }}' + 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: |- + // Strip code block markers if present + const rawLabels = process.env.LABELS_OUTPUT; + core.info(`Raw labels JSON: ${rawLabels}`); + let parsedLabels; + try { + const trimmedLabels = rawLabels.replace(/^```(?:json)?\s*/, '').replace(/\s*```$/, '').trim(); + parsedLabels = JSON.parse(trimmedLabels); + core.info(`Parsed labels JSON: ${JSON.stringify(parsedLabels)}`); + } catch (err) { + core.setFailed(`Failed to parse labels JSON from Gemini output: ${err.message}\nRaw output: ${rawLabels}`); + return; + } + + for (const entry of parsedLabels) { + const issueNumber = entry.issue_number; + if (!issueNumber) { + core.info(`Skipping entry with no issue number: ${JSON.stringify(entry)}`); + continue; + } + + // Set labels based on triage result + if (entry.labels_to_set && entry.labels_to_set.length > 0) { + await github.rest.issues.setLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + labels: entry.labels_to_set + }); + const explanation = entry.explanation ? ` - ${entry.explanation}` : ''; + core.info(`Successfully set labels for #${issueNumber}: ${entry.labels_to_set.join(', ')}${explanation}`); + } else { + // If no labels to set, leave the issue as is + core.info(`No labels to set for #${issueNumber}, leaving as is`); + } + } diff --git a/examples/workflows/pr-review/gemini-pr-review.yml b/examples/workflows/pr-review/gemini-pr-review.yml index 297c4572..3b5bb9bb 100644 --- a/examples/workflows/pr-review/gemini-pr-review.yml +++ b/examples/workflows/pr-review/gemini-pr-review.yml @@ -38,11 +38,17 @@ permissions: jobs: review-pr: + # This condition seeks to ensure the action is only run when it is triggered by a trusted user. + # For private repos, users who have access to the repo are considered trusted. + # For public repos, users who members, owners, or collaborators are considered trusted. if: |- github.event_name == 'workflow_dispatch' || ( github.event_name == 'pull_request' && - contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association) + ( + github.event.repository.private == true || + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association) + ) ) || ( ( @@ -53,16 +59,21 @@ jobs: github.event_name == 'pull_request_review_comment' ) && contains(github.event.comment.body, '@gemini-cli /review') && - contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) + ( + github.event.repository.private == true || + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) + ) ) || ( github.event_name == 'pull_request_review' && contains(github.event.review.body, '@gemini-cli /review') && - contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association) + ( + github.event.repository.private == true || + contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association) + ) ) timeout-minutes: 5 runs-on: 'ubuntu-latest' - steps: - name: 'Checkout PR code' uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 @@ -109,14 +120,14 @@ jobs: } >> "${GITHUB_OUTPUT}" - - name: 'Get PR details (issue_comment)' + - name: 'Get PR details (issue_comment & reviews)' id: 'get_pr_comment' if: |- - ${{ github.event_name == 'issue_comment' }} + ${{ github.event_name == 'issue_comment' || github.event_name == 'pull_request_review' || github.event_name == 'pull_request_review_comment' }} env: GITHUB_TOKEN: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' - COMMENT_BODY: '${{ github.event.comment.body }}' - PR_NUMBER: '${{ github.event.issue.number }}' + COMMENT_BODY: '${{ github.event.comment.body || github.event.review.body }}' + PR_NUMBER: '${{ github.event.issue.number || github.event.pull_request.number }}' run: |- set -euo pipefail @@ -161,6 +172,7 @@ jobs: use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' settings: |- { + "debug": ${{ fromJSON(env.DEBUG || env.ACTIONS_STEP_DEBUG || false) }}, "maxSessionTurns": 20, "mcpServers": { "github": { diff --git a/package-lock.json b/package-lock.json index 79853a30..bd779554 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "run-gemini-cli", - "version": "0.1.10", + "version": "0.1.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "run-gemini-cli", - "version": "0.1.10", + "version": "0.1.11", "license": "Apache-2.0", "devDependencies": { "@google-github-actions/actions-utils": "^0.8.8" diff --git a/package.json b/package.json index 7715b5c1..2cf4864e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "run-gemini-cli", - "version": "0.1.10", + "version": "0.1.11", "description": "This works with our versioning tools, this is NOT an NPM repo", "scripts": { "build": "echo \"No build required for composite action\"",