diff --git a/.github/workflows/gemini-invoke.yml b/.github/workflows/gemini-invoke.yml index e35fd35b8..1d396ec3b 100644 --- a/.github/workflows/gemini-invoke.yml +++ b/.github/workflows/gemini-invoke.yml @@ -61,14 +61,16 @@ jobs: google_api_key: '${{ secrets.GOOGLE_API_KEY }}' use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' + upload_artifacts: '${{ vars.UPLOAD_ARTIFACTS }}' settings: |- { "model": { "maxSessionTurns": 25 }, "telemetry": { - "enabled": ${{ vars.GOOGLE_CLOUD_PROJECT != '' }}, - "target": "gcp" + "enabled": true, + "target": "local", + "outfile": ".gemini/telemetry.log" }, "mcpServers": { "github": { diff --git a/.github/workflows/gemini-issue-fixer.yml b/.github/workflows/gemini-issue-fixer.yml index c23d67704..804d9cc20 100644 --- a/.github/workflows/gemini-issue-fixer.yml +++ b/.github/workflows/gemini-issue-fixer.yml @@ -62,6 +62,7 @@ jobs: google_api_key: '${{ secrets.GOOGLE_API_KEY }}' use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' + upload_artifacts: '${{ vars.UPLOAD_ARTIFACTS }}' settings: |- { "debug": ${{ fromJSON(env.DEBUG || env.ACTIONS_STEP_DEBUG || false) }}, @@ -85,8 +86,9 @@ jobs: } }, "telemetry": { - "enabled": ${{ vars.GOOGLE_CLOUD_PROJECT != '' }}, - "target": "gcp" + "enabled": true, + "target": "local", + "outfile": ".gemini/telemetry.log" } } prompt: '/gemini-issue-fixer' diff --git a/.github/workflows/gemini-review.yml b/.github/workflows/gemini-review.yml index 0a8756093..288a12b46 100644 --- a/.github/workflows/gemini-review.yml +++ b/.github/workflows/gemini-review.yml @@ -63,14 +63,16 @@ jobs: google_api_key: '${{ secrets.GOOGLE_API_KEY }}' use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' + upload_artifacts: '${{ vars.UPLOAD_ARTIFACTS }}' settings: |- { "model": { "maxSessionTurns": 25 }, "telemetry": { - "enabled": ${{ vars.GOOGLE_CLOUD_PROJECT != '' }}, - "target": "gcp" + "enabled": true, + "target": "local", + "outfile": ".gemini/telemetry.log" }, "mcpServers": { "github": { diff --git a/.github/workflows/gemini-scheduled-triage.yml b/.github/workflows/gemini-scheduled-triage.yml index 8cda08a84..912088708 100644 --- a/.github/workflows/gemini-scheduled-triage.yml +++ b/.github/workflows/gemini-scheduled-triage.yml @@ -103,14 +103,16 @@ jobs: google_api_key: '${{ secrets.GOOGLE_API_KEY }}' use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' + upload_artifacts: '${{ vars.UPLOAD_ARTIFACTS }}' settings: |- { "model": { "maxSessionTurns": 25 }, "telemetry": { - "enabled": ${{ vars.GOOGLE_CLOUD_PROJECT != '' }}, - "target": "gcp" + "enabled": true, + "target": "local", + "outfile": ".gemini/telemetry.log" }, "tools": { "core": [ diff --git a/.github/workflows/gemini-triage.yml b/.github/workflows/gemini-triage.yml index 56817510d..6b946c2c9 100644 --- a/.github/workflows/gemini-triage.yml +++ b/.github/workflows/gemini-triage.yml @@ -73,14 +73,16 @@ jobs: google_api_key: '${{ secrets.GOOGLE_API_KEY }}' use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' + upload_artifacts: '${{ vars.UPLOAD_ARTIFACTS }}' settings: |- { "model": { "maxSessionTurns": 25 }, "telemetry": { - "enabled": ${{ vars.GOOGLE_CLOUD_PROJECT != '' }}, - "target": "gcp" + "enabled": true, + "target": "local", + "outfile": ".gemini/telemetry.log" }, "tools": { "core": [ diff --git a/README.md b/README.md index 47663cdde..042b8d195 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,8 @@ go to the [Gemini Assistant workflow documentation](./examples/workflows/gemini- - extensions: _(Optional)_ A list of Gemini CLI extensions to install. +- upload_artifacts: _(Optional, default: `false`)_ Whether or not to upload artifacts to the github action. + diff --git a/action.yml b/action.yml index 081a8487f..af2e46117 100644 --- a/action.yml +++ b/action.yml @@ -71,6 +71,10 @@ inputs: extensions: description: 'A list of Gemini CLI extensions to install.' required: false + upload_artifacts: + description: 'Whether or not to upload artifacts to the github action.' + required: false + default: 'false' outputs: summary: @@ -183,26 +187,6 @@ runs: token_format: 'access_token' access_token_scopes: 'https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/userinfo.profile' - - name: 'Run Telemetry Collector for Google Cloud' - if: |- - ${{ inputs.gcp_workload_identity_provider != '' }} - env: - OTLP_GOOGLE_CLOUD_PROJECT: '${{ inputs.gcp_project_id }}' - GITHUB_ACTION_PATH: '${{ github.action_path }}' - shell: 'bash' - run: |- - set -euo pipefail - mkdir -p .gemini/ - sed "s/OTLP_GOOGLE_CLOUD_PROJECT/${OTLP_GOOGLE_CLOUD_PROJECT}/g" "${GITHUB_ACTION_PATH}/scripts/collector-gcp.yaml.template" > ".gemini/collector-gcp.yaml" - - chmod 444 "$GOOGLE_APPLICATION_CREDENTIALS" - docker run -d --name gemini-telemetry-collector --network host \ - -v "${GITHUB_WORKSPACE}:/github/workspace" \ - -e "GOOGLE_APPLICATION_CREDENTIALS=${GOOGLE_APPLICATION_CREDENTIALS/$GITHUB_WORKSPACE//github/workspace}" \ - -w "/github/workspace" \ - otel/opentelemetry-collector-contrib:0.128.0 \ - --config /github/workspace/.gemini/collector-gcp.yaml - - name: 'Install Gemini CLI' id: 'install' env: @@ -274,22 +258,29 @@ runs: fi fi - GEMINI_RESPONSE="$(cat "${TEMP_STDOUT}")" + # Create the artifacts directory and copy full logs + mkdir -p gemini-artifacts + cp "${TEMP_STDOUT}" gemini-artifacts/stdout.log + cp "${TEMP_STDERR}" gemini-artifacts/stderr.log + if [[ -f .gemini/telemetry.log ]]; then + cp .gemini/telemetry.log gemini-artifacts/telemetry.log + else + # Create an empty file so the artifact upload doesn't fail if telemetry is missing + touch gemini-artifacts/telemetry.log + fi # Set the captured response as a step output, supporting multiline echo "gemini_response<> "${GITHUB_OUTPUT}" - echo "${GEMINI_RESPONSE}" >> "${GITHUB_OUTPUT}" + cat "${TEMP_STDOUT}" >> "${GITHUB_OUTPUT}" echo "EOF" >> "${GITHUB_OUTPUT}" - GEMINI_ERRORS="$(cat "${TEMP_STDERR}")" - # Set the captured errors as a step output, supporting multiline echo "gemini_errors<> "${GITHUB_OUTPUT}" - echo "${GEMINI_ERRORS}" >> "${GITHUB_OUTPUT}" + cat "${TEMP_STDERR}" >> "${GITHUB_OUTPUT}" echo "EOF" >> "${GITHUB_OUTPUT}" if [[ "${FAILED}" = true ]]; then - LAST_LINE="$(echo "${GEMINI_ERRORS}" | tail -n1)" + LAST_LINE="$(tail -n1 "${TEMP_STDERR}")" echo "::error title=Gemini CLI execution failed::${LAST_LINE}" echo "See logs for more details" exit 1 @@ -307,6 +298,82 @@ runs: PROMPT: '${{ inputs.prompt }}' GEMINI_MODEL: '${{ inputs.gemini_model }}' + - name: 'Upload Gemini CLI outputs' + if: |- + ${{ inputs.upload_artifacts }} + uses: 'actions/upload-artifact@v4' # ratchet:exclude + with: + name: 'gemini-output' + path: 'gemini-artifacts/' + + - name: 'Upload Telemetry to Google Cloud' + if: |- + ${{ inputs.gcp_workload_identity_provider != '' }} + shell: 'bash' + run: |- + set -euo pipefail + + # If the telemetry log doesn't exist or is empty, do nothing. + if [[ ! -s ".gemini/telemetry.log" ]]; then + echo "No telemetry log found, skipping upload." + exit 0 + fi + + # Generate the real config file from the template + sed -e "s#OTLP_GOOGLE_CLOUD_PROJECT#${OTLP_GOOGLE_CLOUD_PROJECT}#g" \ + -e "s#GITHUB_REPOSITORY_PLACEHOLDER#${GITHUB_REPOSITORY}#g" \ + -e "s#GITHUB_RUN_ID_PLACEHOLDER#${GITHUB_RUN_ID}#g" \ + "${GITHUB_ACTION_PATH}/scripts/collector-gcp.yaml.template" > ".gemini/collector-gcp.yaml" + + # Ensure credentials file has the right permissions + chmod 444 "$GOOGLE_APPLICATION_CREDENTIALS" + + # Run the collector in the background with a known name + docker run --rm --name gemini-telemetry-collector --network host \ + -v "${GITHUB_WORKSPACE}:/github/workspace" \ + -e "GOOGLE_APPLICATION_CREDENTIALS=${GOOGLE_APPLICATION_CREDENTIALS/$GITHUB_WORKSPACE//github/workspace}" \ + -w "/github/workspace" \ + otel/opentelemetry-collector-contrib:0.108.0 \ + --config /github/workspace/.gemini/collector-gcp.yaml & + + # Wait for the collector to start up + echo "Waiting for collector to initialize..." + sleep 10 + + # Monitor the queue until it's empty or we time out + echo "Monitoring exporter queue..." + ATTEMPTS=0 + MAX_ATTEMPTS=12 # 12 * 5s = 60s timeout + while true; do + # Use -f to fail silently if the server isn't ready yet + # Filter out the prometheus help/type comments before grabbing the value + QUEUE_SIZE=$(curl -sf http://localhost:8888/metrics | grep otelcol_exporter_queue_size | grep -v '^#' | awk '{print $2}' || echo "-1") + + if [ "$QUEUE_SIZE" == "0" ]; then + echo "Exporter queue is empty, all data processed." + break + fi + + if [ "$ATTEMPTS" -ge "$MAX_ATTEMPTS" ]; then + echo "::warning::Timed out waiting for exporter queue to empty. Proceeding with shutdown." + break + fi + + echo "Queue size: $QUEUE_SIZE, waiting..." + sleep 5 + ATTEMPTS=$((ATTEMPTS + 1)) + done + + # Gracefully shut down the collector + echo "Stopping collector..." + docker stop gemini-telemetry-collector + echo "Collector stopped." + env: + OTLP_GOOGLE_CLOUD_PROJECT: '${{ inputs.gcp_project_id }}' + GITHUB_ACTION_PATH: '${{ github.action_path }}' + GITHUB_REPOSITORY: '${{ github.repository }}' + GITHUB_RUN_ID: '${{ github.run_id }}' + branding: icon: 'terminal' color: 'blue' diff --git a/scripts/collector-gcp.yaml.template b/scripts/collector-gcp.yaml.template index 06cc80e28..ba3c157d5 100644 --- a/scripts/collector-gcp.yaml.template +++ b/scripts/collector-gcp.yaml.template @@ -1,34 +1,33 @@ receivers: - otlp: - protocols: - grpc: - endpoint: 'localhost:4317' + filelog: + include: ['.gemini/telemetry.log'] + start_at: 'beginning' + processors: + resource: + attributes: + - key: 'github.repository' + value: 'GITHUB_REPOSITORY_PLACEHOLDER' + action: 'upsert' + - key: 'github.run_id' + value: 'GITHUB_RUN_ID_PLACEHOLDER' + action: 'upsert' batch: - timeout: '1s' + send_batch_size: 100 + timeout: '10s' + exporters: googlecloud: project: 'OTLP_GOOGLE_CLOUD_PROJECT' - metric: - prefix: 'custom.googleapis.com/gemini_cli' log: - default_log_name: 'gemini_cli' + default_log_name: 'gemini-cli' + service: - telemetry: - logs: - level: 'debug' - metrics: - level: 'none' pipelines: - traces: - receivers: ['otlp'] - processors: ['batch'] - exporters: ['googlecloud'] - metrics: - receivers: ['otlp'] - processors: ['batch'] - exporters: ['googlecloud'] logs: - receivers: ['otlp'] - processors: ['batch'] + receivers: ['filelog'] + processors: ['batch', 'resource'] exporters: ['googlecloud'] + telemetry: + metrics: + address: '0.0.0.0:8888'