google-github-actions / google-github-actions/run-gemini-cli
Summary output field contains invalid JSON when using json output format
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2.1k
- Forks
- 285
- Avg merge
- 8h 8m
- Merged PRs (30d)
- 1
Description
TL;DR
The summary output field in the action contains invalid JSON when you enable json output format in the configuration, like this:
{
"output": {
"format": "json"
}
}
This is a bug in the multi line field handling logic where the output file is cat'ed to GITHUB_OUTPUT but it is not done correctly.
Expected behavior
The summary output field should contain valid JSON. For example:
{
"response": "I have submitted the review. My task is complete.",
"stats": {}
}
Observed behavior
There are extra characters appended at the end, like this:
{
"response": "I have submitted the review. My task is complete.",
"stats": {}
}EOF
gemini_errors<<EOF
Action YAML
name: "🔎 Review"
description: "Gemini-powered PR review."
inputs:
github_app_id:
description: "GitHub App ID"
required: true
github_app_private_key:
description: "GitHub App private key PEM for the Bucky app"
required: true
gemini_api_key:
description: "Gemini API key"
required: true
additional_context:
description: "Any additional context from the request"
required: false
default: ""
gemini_cli_version:
description: "Gemini CLI version"
required: false
default: ""
gemini_model:
description: "Gemini model"
required: false
default: ""
runs:
using: "composite"
steps:
- name: Mint identity token
id: mint_identity_token
uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b
with:
app-id: ${{ inputs.github_app_id }}
private-key: ${{ inputs.github_app_private_key }}
permission-contents: read
permission-issues: write
permission-pull-requests: write
- name: Run Gemini pull request review
id: gemini_pr_review
uses: google-github-actions/run-gemini-cli@v0
env:
GITHUB_TOKEN: ${{ steps.mint_identity_token.outputs.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:
gemini_api_key: ${{ inputs.gemini_api_key }}
gemini_cli_version: ${{ inputs.gemini_cli_version }}
gemini_model: ${{ inputs.gemini_model }}
gemini_debug: false
settings: |
{
"general": {
"disableAutoUpdate": false
},
"output": {
"format": "json"
},
"context": {
"fileName": "AGENTS.md"
},
"model": {
"maxSessionTurns": 25,
"chatCompression": {
"contextPercentageThreshold": 0.7
}
},
"telemetry": {
"enabled": false
},
"privacy": {
"usageStatisticsEnabled": false
},
"ui": {
"showMemoryUsage": true
},
"mcpServers": {
"github": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server:v0.19.0"
],
"includeTools": [
"add_comment_to_pending_review",
"pull_request_read",
"pull_request_review_write"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
}
}
},
"tools": {
"core": [
"glob",
"google_web_search",
"list_directory",
"read_file",
"read_many_files",
"run_shell_command(cat)",
"run_shell_command(echo)",
"run_shell_command(git)",
"run_shell_command(grep)",
"run_shell_command(head)",
"run_shell_command(nl)",
"run_shell_command(tail)",
"search_file_content",
"web_fetch"
]
}
}
prompt: |-
...
- name: Process summary
id: process_summary
shell: bash
if: always()
env:
GEMINI_SUMMARY: ${{ steps.gemini_pr_review.outputs.summary }}
run: |
set -uo pipefail
echo "::group::Process summary"
# the output can have junk after the JSON structure so use jq to remove it by having it parse from { to }
JSON_OBJ="$(echo "$GEMINI_SUMMARY" | jq -R -s 'match("(?s)\\{.*\\}") | .string | fromjson')"
# verify it
echo "$JSON_OBJ" | jq empty
# safely output multiline string
DELIMITER="__GEMINI_EOF__"
{
echo "json<<${DELIMITER}"
echo "${JSON_OBJ}"
echo "${DELIMITER}"
} >> "${GITHUB_OUTPUT}"
echo "::endgroup::"
- name: Logs
if: always()
shell: bash
env:
GEMINI_SUMMARY: ${{ steps.gemini_pr_review.outputs.summary }}
GEMINI_SUMMARY_JSON: ${{ steps.process_summary.outputs.json }}
GEMINI_ERROR: ${{ steps.gemini_pr_review.outputs.error }}
run: |
set -uo pipefail
# show the plain response in text
echo "::group::Gemini response"
echo "$GEMINI_SUMMARY_JSON" | jq -r .response
echo "::endgroup::"
# dump the entire summary
echo "::group::Gemini output"
echo "$GEMINI_SUMMARY"
echo "::endgroup::"
# dump the errors
echo "::group::Gemini errors"
echo "$GEMINI_ERROR"
echo "::endgroup::"
Log output
Run google-github-actions/run-gemini-cli@v0
Run set -exuo pipefail
+ auth_methods=0
+ [[ true == \t\r\u\e ]]
+ (( ++auth_methods ))
+ [[ false == \t\r\u\e ]]
+ [[ false == \t\r\u\e ]]
+ [[ 1 -eq 0 ]]
+ [[ 1 -gt 1 ]]
+ [[ false == \t\r\u\e ]]
+ [[ false == \t\r\u\e ]]
+ [[ true == \t\r\u\e ]]
+ [[ false == \t\r\u\e ]]
+ [[ false == \t\r\u\e ]]
Run mkdir -p .gemini/
Run set -euo pipefail
Run set -euo pipefail
Installing Gemini CLI from npm: @google/gemini-cli@latest
Verifying installation:
0.10.0
Run set -euo pipefail
Run set -uo pipefail
Process summary
Run set -uo pipefail
Gemini response
I have submitted the review. My task is complete.
Gemini output
{
"response": "I have submitted the review. My task is complete.",
"stats": {
"models": {
"gemini-2.5-pro": {
"api": {
"totalRequests": 8,
"totalErrors": 0,
"totalLatencyMs": 40355
},
"tokens": {
"prompt": 280112,
"candidates": 612,
"total": 282069,
"cached": 194828,
"thoughts": 1345,
"tool": 0
}
}
},
"tools": {
"totalCalls": 7,
"totalSuccess": 7,
"totalFail": 0,
"totalDurationMs": 5691,
"totalDecisions": {
"accept": 0,
"reject": 0,
"modify": 0,
"auto_accept": 7
},
"byName": {
"read_file": {
"count": 1,
"success": 1,
"fail": 0,
"durationMs": 5,
"decisions": {
"accept": 0,
"reject": 0,
"modify": 0,
"auto_accept": 1
}
},
"pull_request_read": {
"count": 2,
"success": 2,
"fail": 0,
"durationMs": 822,
"decisions": {
"accept": 0,
"reject": 0,
"modify": 0,
"auto_accept": 2
}
},
"pull_request_review_write": {
"count": 2,
"success": 2,
"fail": 0,
"durationMs": 2408,
"decisions": {
"accept": 0,
"reject": 0,
"modify": 0,
"auto_accept": 2
}
},
"add_comment_to_pending_review": {
"count": 2,
"success": 2,
"fail": 0,
"durationMs": 2456,
"decisions": {
"accept": 0,
"reject": 0,
"modify": 0,
"auto_accept": 2
}
}
}
},
"files": {
"totalLinesAdded": 0,
"totalLinesRemoved": 0
}
}
}EOF
gemini_errors<<EOF
Gemini errors
Additional information
The problem is here, with these lines in action.yml, ~line 272:
# Set the captured response as a step output, supporting multiline
echo "gemini_response<<EOF" >> "${GITHUB_OUTPUT}"
cat "${TEMP_STDOUT}" >> "${GITHUB_OUTPUT}"
echo "EOF" >> "${GITHUB_OUTPUT}"
This does not terminate the redirection correctly and ends up picking up the extra output until the next redirection is started. This works correctly:
# safely output multiline string using a more unique delimiter
DELIMITER="__GEMINI_EOF__"
{
echo "gemini_response<<${DELIMITER}"
cat "${TEMP_STDOUT}"
echo "${DELIMITER}"
} >> "${GITHUB_OUTPUT}"
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in action.yml around line 272, where the captured response is written to GITHUB_OUTPUT using the EOF delimiter. Reproduce the JSON-format workflow and inspect the generated summary output. Done means the summary parses as valid JSON and does not include trailing error output.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- github-actions, shell
- Domain
- ci-cd, devops
- Issue type
- Bug
- Difficulty
- 1/5
- Estimated time
- Under an hour
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 50/100