forked from google-github-actions/run-gemini-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaction.yml
More file actions
557 lines (503 loc) · 22.7 KB
/
action.yml
File metadata and controls
557 lines (503 loc) · 22.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: 'Run Gemini CLI'
author: 'Google LLC'
description: |-
Invoke the Gemini CLI from a GitHub Action.
inputs:
gcp_location:
description: 'The Google Cloud location.'
required: false
gcp_project_id:
description: 'The Google Cloud project ID.'
required: false
gcp_service_account:
description: 'The Google Cloud service account email.'
required: false
gcp_workload_identity_provider:
description: 'The Google Cloud Workload Identity Provider.'
required: false
gcp_token_format:
description: 'The token format for authentication. Set to "access_token" to generate access tokens (requires service account), or set to empty string for direct WIF. Can be "access_token" or "id_token".'
required: false
default: 'access_token'
gcp_access_token_scopes:
description: 'The access token scopes when using token_format "access_token". Comma-separated list of OAuth 2.0 scopes.'
required: false
default: 'https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/userinfo.profile'
gemini_api_key:
description: 'The API key for the Gemini API.'
required: false
gemini_cli_version:
description: 'The version of the Gemini CLI to install. Can be "latest", "preview", "nightly", a specific version number, or a git branch, tag, or commit. For more information, see [Gemini CLI releases](https://github.com/google-gemini/gemini-cli/blob/main/docs/releases.md).'
required: false
default: 'latest'
gemini_debug:
description: 'Enable debug logging and output streaming.'
required: false
gemini_model:
description: 'The model to use with Gemini.'
required: false
google_api_key:
description: 'The Vertex AI API key to use with Gemini.'
required: false
prompt:
description: |-
A string passed to the Gemini CLI's [`--prompt` argument](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/configuration.md#command-line-arguments).
required: false
default: 'You are a helpful assistant.'
settings:
description: |-
A JSON string written to `.gemini/settings.json` to configure the CLI's _project_ settings.
For more details, see the documentation on [settings files](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/configuration.md#settings-files).
required: false
use_gemini_code_assist:
description: |-
Whether to use Code Assist for Gemini model access instead of the default Gemini API key.
For more information, see the [Gemini CLI documentation](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/authentication.md).
required: false
default: 'false'
use_vertex_ai:
description: |-
Whether to use Vertex AI for Gemini model access instead of the default Gemini API key.
For more information, see the [Gemini CLI documentation](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/authentication.md).
required: false
default: 'false'
extensions:
description: 'A list of Gemini CLI extensions to install.'
required: false
upload_artifacts:
description: 'Whether to upload artifacts to the github action.'
required: false
default: 'false'
use_pnpm:
description: 'Whether or not to use pnpm instead of npm to install gemini-cli'
required: false
default: 'false'
workflow_name:
description: 'The GitHub workflow name, used for telemetry purposes.'
required: false
default: '${{ github.workflow }}'
outputs:
summary:
description: 'The summarized output from the Gemini CLI execution.'
value: '${{ steps.gemini_run.outputs.gemini_response }}'
error:
description: 'The error output from the Gemini CLI execution, if any.'
value: '${{ steps.gemini_run.outputs.gemini_errors }}'
runs:
using: 'composite'
steps:
- name: 'Validate Inputs'
id: 'validate_inputs'
shell: 'bash'
run: |-
set -exuo pipefail
# Emit a clear warning in three places without failing the step
warn() {
local msg="$1"
echo "WARNING: ${msg}" >&2
echo "::warning title=Input validation::${msg}"
if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then
{
echo "### Input validation warnings"
echo
echo "- ${msg}"
} >> "${GITHUB_STEP_SUMMARY}"
fi
}
# Validate the count of authentication methods
auth_methods=0
if [[ "${INPUT_GEMINI_API_KEY_PRESENT:-false}" == "true" ]]; then ((++auth_methods)); fi
if [[ "${INPUT_GOOGLE_API_KEY_PRESENT:-false}" == "true" ]]; then ((++auth_methods)); fi
if [[ "${INPUT_GCP_WORKLOAD_IDENTITY_PROVIDER_PRESENT:-false}" == "true" ]]; then ((++auth_methods)); fi
if [[ ${auth_methods} -eq 0 ]]; then
warn "No authentication method provided. Please provide one of 'gemini_api_key', 'google_api_key', or 'gcp_workload_identity_provider'."
fi
if [[ ${auth_methods} -gt 1 ]]; then
warn "Multiple authentication methods provided. Please use only one of 'gemini_api_key', 'google_api_key', or 'gcp_workload_identity_provider'."
fi
# Validate Workload Identity Federation inputs
if [[ "${INPUT_GCP_WORKLOAD_IDENTITY_PROVIDER_PRESENT:-false}" == "true" ]]; then
if [[ "${INPUT_GCP_PROJECT_ID_PRESENT:-false}" != "true" ]]; then
warn "When using Workload Identity Federation ('gcp_workload_identity_provider'), you must also provide 'gcp_project_id'."
fi
# Service account is required when using token_format (default behavior)
# Only optional when explicitly set to empty for direct WIF
if [[ "${INPUT_GCP_TOKEN_FORMAT}" != "" && "${INPUT_GCP_SERVICE_ACCOUNT_PRESENT:-false}" != "true" ]]; then
warn "When using Workload Identity Federation with token generation ('gcp_token_format'), you must also provide 'gcp_service_account'. To use direct WIF without a service account, explicitly set 'gcp_token_format' to an empty string."
fi
if [[ "${INPUT_USE_VERTEX_AI:-false}" == "${INPUT_USE_GEMINI_CODE_ASSIST:-false}" ]]; then
warn "When using Workload Identity Federation, you must set exactly one of 'use_vertex_ai' or 'use_gemini_code_assist' to 'true'."
fi
fi
# Validate Vertex AI API Key
if [[ "${INPUT_GOOGLE_API_KEY_PRESENT:-false}" == "true" ]]; then
if [[ "${INPUT_USE_VERTEX_AI:-false}" != "true" ]]; then
warn "When using 'google_api_key', you must set 'use_vertex_ai' to 'true'."
fi
if [[ "${INPUT_USE_GEMINI_CODE_ASSIST:-false}" == "true" ]]; then
warn "When using 'google_api_key', 'use_gemini_code_assist' cannot be 'true'."
fi
fi
# Validate Gemini API Key
if [[ "${INPUT_GEMINI_API_KEY_PRESENT:-false}" == "true" ]]; then
if [[ "${INPUT_USE_VERTEX_AI:-false}" == "true" || "${INPUT_USE_GEMINI_CODE_ASSIST:-false}" == "true" ]]; then
warn "When using 'gemini_api_key', both 'use_vertex_ai' and 'use_gemini_code_assist' must be 'false'."
fi
fi
env:
INPUT_GEMINI_API_KEY_PRESENT: "${{ inputs.gemini_api_key != '' }}"
INPUT_GOOGLE_API_KEY_PRESENT: "${{ inputs.google_api_key != '' }}"
INPUT_GCP_WORKLOAD_IDENTITY_PROVIDER_PRESENT: "${{ inputs.gcp_workload_identity_provider != '' }}"
INPUT_GCP_PROJECT_ID_PRESENT: "${{ inputs.gcp_project_id != '' }}"
INPUT_GCP_SERVICE_ACCOUNT_PRESENT: "${{ inputs.gcp_service_account != '' }}"
INPUT_GCP_TOKEN_FORMAT: '${{ inputs.gcp_token_format }}'
INPUT_USE_VERTEX_AI: '${{ inputs.use_vertex_ai }}'
INPUT_USE_GEMINI_CODE_ASSIST: '${{ inputs.use_gemini_code_assist }}'
- name: 'Sanitize workflow name'
id: 'sanitize_workflow_name'
shell: 'bash'
run: |
SANITIZED=$(echo "${WORKFLOW_NAME}" | sed 's/[^ a-zA-Z0-9-]//g' | xargs | tr ' ' '_' | tr '[:upper:]' '[:lower:]')
echo "gh_workflow_name=$SANITIZED" >> $GITHUB_OUTPUT
env:
WORKFLOW_NAME: '${{ inputs.workflow_name }}'
- name: 'Configure Gemini CLI'
if: |-
${{ inputs.settings != '' }}
run: |-
mkdir -p .gemini/
echo "${SETTINGS}" > ".gemini/settings.json"
shell: 'bash'
env:
SETTINGS: '${{ inputs.settings }}'
- name: 'Install Custom Commands'
shell: 'bash'
run: |-
set -euo pipefail
DEST_DIR=".gemini/commands"
mkdir -p "$DEST_DIR"
ACTION_SRC="${GITHUB_ACTION_PATH}/.github/commands"
USER_SRC="${GITHUB_WORKSPACE}/.github/commands"
install_commands() {
local src="$1"
local source_name="$2"
if [[ -d "$src" ]]; then
if [ "$(ls -A "$src")" ]; then
echo "Installing ${source_name}..."
cp -r "$src/." "$DEST_DIR/"
else
echo "Found ${source_name} directory but it is empty. Skipping."
fi
else
echo "No ${source_name} found at $src. Skipping."
fi
}
install_commands "$ACTION_SRC" "Default commands (from Action)"
install_commands "$USER_SRC" "Custom commands (from User Repository)"
echo "Final list of installed commands:"
ls -la "$DEST_DIR"
env:
GITHUB_ACTION_PATH: '${{ github.action_path }}'
GITHUB_WORKSPACE: '${{ github.workspace }}'
- name: 'Add Gemini Context'
shell: 'bash'
run: |-
set -euo pipefail
# Path to the GEMINI.md in the Action's root directory
ACTION_CONTEXT="${GITHUB_ACTION_PATH}/GEMINI.md"
# Path to the GEMINI.md in the User's workspace root
USER_CONTEXT="${GITHUB_WORKSPACE}/GEMINI.md"
if [[ -f "$ACTION_CONTEXT" ]]; then
if [[ -f "$USER_CONTEXT" ]]; then
echo "Found existing GEMINI.md in repository. Prepending default context from Action..."
# Prepend logic: Concatenate Action context + User context into a temp file, then overwrite user file
# Adding a newline between files to ensure separation
cat "$ACTION_CONTEXT" <(echo) "$USER_CONTEXT" > "${USER_CONTEXT}.tmp"
mv "${USER_CONTEXT}.tmp" "$USER_CONTEXT"
else
echo "No GEMINI.md found in repository. Copying default context from Action..."
cp "$ACTION_CONTEXT" "$USER_CONTEXT"
fi
echo "Gemini Context (GEMINI.md) successfully prepared."
echo ""
echo ">>> START OF GEMINI.md CONTENT <<<"
cat "$USER_CONTEXT"
echo ">>> END OF GEMINI.md CONTENT <<<"
echo ""
else
echo "No default GEMINI.md found in Action root ($ACTION_CONTEXT). Skipping context addition."
fi
env:
GITHUB_ACTION_PATH: '${{ github.action_path }}'
GITHUB_WORKSPACE: '${{ github.workspace }}'
- name: 'Authenticate to Google Cloud'
if: |-
${{ inputs.gcp_workload_identity_provider != '' }}
id: 'auth'
uses: 'google-github-actions/auth@v3' # ratchet:exclude
with:
project_id: '${{ inputs.gcp_project_id }}'
workload_identity_provider: '${{ inputs.gcp_workload_identity_provider }}'
service_account: '${{ inputs.gcp_service_account }}'
token_format: '${{ inputs.gcp_token_format }}'
access_token_scopes: '${{ inputs.gcp_access_token_scopes }}'
- name: 'Install pnpm'
if: |-
${{ inputs.use_pnpm == 'true' }}
uses: 'pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061' # ratchet:pnpm/action-setup@v4
with:
version: 10
- name: 'Install Gemini CLI'
id: 'install'
env:
GEMINI_CLI_VERSION: '${{ inputs.gemini_cli_version }}'
EXTENSIONS: '${{ inputs.extensions }}'
USE_PNPM: '${{ inputs.use_pnpm }}'
shell: 'bash'
run: |-
set -euo pipefail
VERSION_INPUT="${GEMINI_CLI_VERSION:-latest}"
if [[ "${VERSION_INPUT}" == "latest" || "${VERSION_INPUT}" == "preview" || "${VERSION_INPUT}" == "nightly" || "${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}"
if [[ "${USE_PNPM}" == "true" ]]; then
pnpm add --silent --global @google/gemini-cli@"${VERSION_INPUT}"
else
npm install --silent --no-audit --prefer-offline --global @google/gemini-cli@"${VERSION_INPUT}"
fi
else
echo "Installing Gemini CLI from GitHub: github:google-gemini/gemini-cli#${VERSION_INPUT}"
git clone https://github.com/google-gemini/gemini-cli.git
cd gemini-cli
git checkout "${VERSION_INPUT}"
npm install
npm run bundle
npm install --silent --no-audit --prefer-offline --global .
fi
echo "Verifying installation:"
if command -v gemini >/dev/null 2>&1; then
gemini --version || echo "Gemini CLI installed successfully (version command not available)"
else
echo "Error: Gemini CLI not found in PATH"
exit 1
fi
if [[ -n "${EXTENSIONS}" ]]; then
echo "Installing Gemini CLI extensions:"
echo "${EXTENSIONS}" | jq -r '.[]' | while IFS= read -r extension; do
extension=$(echo "${extension}" | xargs)
if [[ -n "${extension}" ]]; then
echo "Installing ${extension}..."
echo "Y" | gemini extensions install "${extension}"
fi
done
fi
- name: 'Run Gemini CLI'
id: 'gemini_run'
shell: 'bash'
run: |-
set -euo pipefail
# Create a temporary directory for storing the output, and ensure it's
# cleaned up later
TEMP_STDOUT="$(mktemp -p "${RUNNER_TEMP}" gemini-out.XXXXXXXXXX)"
TEMP_STDERR="$(mktemp -p "${RUNNER_TEMP}" gemini-err.XXXXXXXXXX)"
function cleanup {
rm -f "${TEMP_STDOUT}" "${TEMP_STDERR}"
}
trap cleanup EXIT
# Keep track of whether we've failed
FAILED=false
# Run Gemini CLI with the provided prompt, using JSON output format
# We capture stdout (JSON) to TEMP_STDOUT and stderr to TEMP_STDERR
if [[ "${GEMINI_DEBUG}" = true ]]; then
echo "::warning::Gemini CLI debug logging is enabled. This will stream responses, which could reveal sensitive information if processed with untrusted inputs."
echo "::: Start Gemini CLI STDOUT :::"
if ! gemini --debug --yolo --prompt "${PROMPT}" --output-format json 2> >(tee "${TEMP_STDERR}" >&2) | tee "${TEMP_STDOUT}"; then
FAILED=true
fi
# Wait for async stderr logging to complete. This is because process substitution in Bash is async so let tee finish writing to ${TEMP_STDERR}
sleep 1
echo "::: End Gemini CLI STDOUT :::"
else
if ! gemini --yolo --prompt "${PROMPT}" --output-format json 2> "${TEMP_STDERR}" 1> "${TEMP_STDOUT}"; then
FAILED=true
fi
fi
# 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
# Parse JSON output to extract response and errors
# If output is not valid JSON, RESPONSE will be empty and we'll rely on stderr for errors
RESPONSE=""
ERROR_JSON=""
if jq -e . "${TEMP_STDOUT}" >/dev/null 2>&1; then
RESPONSE=$(jq -r '.response // ""' "${TEMP_STDOUT}")
fi
if jq -e . "${TEMP_STDERR}" >/dev/null 2>&1; then
ERROR_JSON=$(jq -c '.error // empty' "${TEMP_STDERR}")
fi
if { [[ -s "${TEMP_STDERR}" ]] && ! jq -e . "${TEMP_STDERR}" >/dev/null 2>&1; }; then
echo "::warning::Gemini CLI stderr was not valid JSON"
fi
if { [[ -s "${TEMP_STDOUT}" ]] && ! jq -e . "${TEMP_STDOUT}" >/dev/null 2>&1; }; then
echo "::warning::Gemini CLI stdout was not valid JSON"
fi
# Set the captured response as a step output, supporting multiline
echo "gemini_response<<EOF" >> "${GITHUB_OUTPUT}"
if [[ -n "${RESPONSE}" ]]; then
echo "${RESPONSE}" >> "${GITHUB_OUTPUT}"
else
cat "${TEMP_STDOUT}" >> "${GITHUB_OUTPUT}"
fi
echo "EOF" >> "${GITHUB_OUTPUT}"
# Set the captured errors as a step output, supporting multiline
echo "gemini_errors<<EOF" >> "${GITHUB_OUTPUT}"
if [[ -n "${ERROR_JSON}" ]]; then
echo "${ERROR_JSON}" >> "${GITHUB_OUTPUT}"
else
cat "${TEMP_STDERR}" >> "${GITHUB_OUTPUT}"
fi
echo "EOF" >> "${GITHUB_OUTPUT}"
# Generate Job Summary
if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then
{
echo "### Gemini CLI Execution"
echo
echo "#### Prompt"
echo
echo "\`\`\`"
echo "${PROMPT}"
echo "\`\`\`"
echo
if [[ -n "${RESPONSE}" ]]; then
echo "#### Response"
echo
echo "${RESPONSE}"
echo
fi
if [[ -n "${ERROR_JSON}" ]]; then
echo "#### Error"
echo
echo "\`\`\`json"
echo "${ERROR_JSON}"
echo "\`\`\`"
echo
elif [[ "${FAILED}" == "true" ]]; then
echo "#### Error Output"
echo
echo "\`\`\`"
cat "${TEMP_STDERR}"
echo "\`\`\`"
echo
fi
} >> "${GITHUB_STEP_SUMMARY}"
fi
if [[ "${FAILED}" = true ]]; then
# If we have a structured error from JSON, use it for the error message
if [[ -n "${ERROR_JSON}" ]]; then
ERROR_MSG=$(jq -r '.message // .' <<< "${ERROR_JSON}")
echo "::error title=Gemini CLI execution failed::${ERROR_MSG}"
fi
echo "::: Start Gemini CLI STDERR :::"
cat "${TEMP_STDERR}"
echo "::: End Gemini CLI STDERR :::"
exit 1
fi
env:
GEMINI_DEBUG: '${{ fromJSON(inputs.gemini_debug || false) }}'
GEMINI_API_KEY: '${{ inputs.gemini_api_key }}'
SURFACE: 'GitHub'
GOOGLE_CLOUD_PROJECT: '${{ inputs.gcp_project_id }}'
GOOGLE_CLOUD_LOCATION: '${{ inputs.gcp_location }}'
GOOGLE_GENAI_USE_VERTEXAI: '${{ inputs.use_vertex_ai }}'
GOOGLE_API_KEY: '${{ inputs.google_api_key }}'
GOOGLE_GENAI_USE_GCA: '${{ inputs.use_gemini_code_assist }}'
GOOGLE_CLOUD_ACCESS_TOKEN: '${{steps.auth.outputs.access_token}}'
PROMPT: '${{ inputs.prompt }}'
GEMINI_MODEL: '${{ inputs.gemini_model }}'
GH_WORKFLOW_NAME: '${{ steps.sanitize_workflow_name.outputs.gh_workflow_name }}'
- name: 'Upload Gemini CLI outputs'
if: |-
${{ inputs.upload_artifacts == 'true' }}
uses: 'actions/upload-artifact@v6' # 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'