mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-08-06 23:23:36 -07:00
feat(pr-generator-infra): configure Cloud Run job, Workflows definition, and Dockerfile (#28431)
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
# Dockerfile for Jetski/Antigravity Worker Job using the Python SDK
|
||||
# This container runs the manager script (workflow/worker.py) which orchestrates
|
||||
# the code generation and evaluation in-process using google-antigravity and Firestore synchronization.
|
||||
|
||||
FROM python:3.11-slim
|
||||
|
||||
# 1. Install system utilities (git is required for repository operations)
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy Node.js 20 and npm directly from the official node:20-slim image
|
||||
COPY --from=node:20-slim /usr/local/bin/node /usr/local/bin/node
|
||||
COPY --from=node:20-slim /usr/local/lib/node_modules /usr/local/lib/node_modules
|
||||
RUN ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && \
|
||||
ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx
|
||||
|
||||
# 2. Create non-root system user and establish working directory
|
||||
RUN useradd -m -u 1000 appuser
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 3. Install Python dependencies using requirements.txt for optimized build layer caching
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir --upgrade pip && \
|
||||
pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
|
||||
# 4. Copy the pipeline orchestration script and prompts with non-root ownership
|
||||
COPY --chown=appuser:appuser workflow/ /app/workflow/
|
||||
COPY --chown=appuser:appuser agent_prompts/ /app/agent_prompts/
|
||||
|
||||
# 5. Switch to unprivileged user
|
||||
USER appuser
|
||||
|
||||
# 6. Execute script directly
|
||||
ENTRYPOINT ["python", "/app/workflow/worker.py"]
|
||||
@@ -0,0 +1,40 @@
|
||||
apiVersion: 'run.googleapis.com/v1'
|
||||
kind: 'Job'
|
||||
metadata:
|
||||
labels:
|
||||
cloud.googleapis.com/location: 'us-central1'
|
||||
name: 'pr-gen-job'
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
run.googleapis.com/client-name: 'gcloud'
|
||||
run.googleapis.com/client-version: '575.0.1'
|
||||
run.googleapis.com/execution-environment: 'gen2'
|
||||
spec:
|
||||
taskCount: 1
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: 'GOOGLE_CLOUD_LOCATION'
|
||||
value: 'global'
|
||||
- name: 'MODEL_NAME'
|
||||
value: 'gemini-3.5-flash'
|
||||
- name: 'FIRESTORE_DATABASE'
|
||||
value: 'gcli-db'
|
||||
- name: 'FIRESTORE_COLLECTION'
|
||||
value: 'issues'
|
||||
- name: 'GIT_TOKEN'
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
key: 'latest'
|
||||
name: 'PR_GEN_GITHUB_PUSH_KEY'
|
||||
image: 'us-central1-docker.pkg.dev/gcli-intern-project-2026/pr-gen-repo/jetski-worker:latest'
|
||||
resources:
|
||||
limits:
|
||||
cpu: '2'
|
||||
memory: '8Gi'
|
||||
maxRetries: 2
|
||||
serviceAccountName: 'code-gen-job-execution-sa@gcli-intern-project-2026.iam.gserviceaccount.com'
|
||||
timeoutSeconds: '3600'
|
||||
@@ -0,0 +1,6 @@
|
||||
google-antigravity>=0.1.0
|
||||
protobuf>=7.35.0
|
||||
pydantic
|
||||
google-cloud-firestore>=2.15.0, <3.0.0
|
||||
google-cloud-storage>=2.14.0
|
||||
google-genai>=2.0.0
|
||||
@@ -0,0 +1,103 @@
|
||||
# Google Cloud Workflow that triggers Cloud Run Job and updates Firestore on failure.
|
||||
|
||||
main:
|
||||
params: ['event']
|
||||
steps:
|
||||
- init:
|
||||
assign:
|
||||
- project_id: '${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}'
|
||||
- database_id: '${default(sys.get_env("FIRESTORE_DATABASE"), "gcli-db")}'
|
||||
- collection_name: '${default(sys.get_env("FIRESTORE_COLLECTION"), "issues")}'
|
||||
- job_name: 'pr-gen-job' # The Cloud Run Job name
|
||||
- job_location: 'us-central1'
|
||||
- workflow_execution_id: '${sys.get_env("GOOGLE_CLOUD_WORKFLOW_EXECUTION_ID")}'
|
||||
|
||||
# Decode Pub/Sub message
|
||||
- pubsub_message_bytes: '${base64.decode(event.data.message.data)}'
|
||||
- firestore_doc_str: '${text.decode(pubsub_message_bytes)}'
|
||||
- firestore_doc: '${json.decode(firestore_doc_str)}'
|
||||
- doc_id: '${"github_" + firestore_doc.github_metadata.owner + "_" + firestore_doc.github_metadata.repo + "_" + string(firestore_doc.github_metadata.issue_number)}'
|
||||
- repo_url: '${"https://github.com/" + firestore_doc.github_metadata.owner + "/" + firestore_doc.github_metadata.repo}'
|
||||
- status: 'STARTED'
|
||||
- error_details: null
|
||||
|
||||
- validate_doc_id:
|
||||
switch:
|
||||
- condition: '${not text.match_regex(doc_id, "^[a-zA-Z0-9_.-]+$")}'
|
||||
raise: '${"Security Exception: Invalid or unsafe firestore_id format: " + string(doc_id)}'
|
||||
|
||||
- run_job:
|
||||
try:
|
||||
call: 'googleapis.run.v1.namespaces.jobs.run'
|
||||
args:
|
||||
name: '${"namespaces/" + project_id + "/jobs/" + job_name}'
|
||||
location: '${job_location}'
|
||||
body:
|
||||
overrides:
|
||||
containerOverrides:
|
||||
- env:
|
||||
- name: 'FIRESTORE_DOC'
|
||||
value: '${firestore_doc_str}'
|
||||
- name: 'REPO_URL'
|
||||
value: '${repo_url}'
|
||||
- name: 'USE_ADC'
|
||||
value: 'true'
|
||||
- name: 'EXECUTION_ID'
|
||||
value: '${sys.get_env("GOOGLE_CLOUD_WORKFLOW_EXECUTION_ID")}'
|
||||
- name: 'GOOGLE_CLOUD_WORKFLOW_EXECUTION_ID'
|
||||
value: '${sys.get_env("GOOGLE_CLOUD_WORKFLOW_EXECUTION_ID")}'
|
||||
- name: 'FIRESTORE_ID'
|
||||
value: '${doc_id}'
|
||||
connector_params:
|
||||
timeout: 7200
|
||||
result: 'job_result'
|
||||
except:
|
||||
as: 'e'
|
||||
steps:
|
||||
- handle_job_error:
|
||||
assign:
|
||||
- status: 'NEEDS_HUMAN'
|
||||
- error_details: '${e}'
|
||||
- update_firestore_on_failure:
|
||||
try:
|
||||
call: 'googleapis.firestore.v1.projects.databases.documents.patch'
|
||||
args:
|
||||
name: '${"projects/" + project_id + "/databases/" + database_id + "/documents/" + collection_name + "/" + doc_id}'
|
||||
updateMask:
|
||||
fieldPaths:
|
||||
- 'status'
|
||||
- 'error'
|
||||
- 'lock.holder'
|
||||
- 'lock.expires_at'
|
||||
body:
|
||||
fields:
|
||||
status:
|
||||
stringValue: 'NEEDS_HUMAN'
|
||||
error:
|
||||
stringValue: '${"Workflow Execution " + sys.get_env("GOOGLE_CLOUD_WORKFLOW_EXECUTION_ID") + " failed: " + json.encode_to_string(error_details)}'
|
||||
lock:
|
||||
mapValue:
|
||||
fields:
|
||||
holder:
|
||||
nullValue: 'NULL_VALUE'
|
||||
expires_at:
|
||||
nullValue: 'NULL_VALUE'
|
||||
except:
|
||||
as: 'fs_err'
|
||||
steps:
|
||||
- log_firestore_error:
|
||||
assign:
|
||||
- firestore_error: '${fs_err}'
|
||||
next: 'handle_failure'
|
||||
next: 'mark_completed'
|
||||
|
||||
- handle_failure:
|
||||
return: '${"Failed: " + json.encode_to_string(error_details)}'
|
||||
|
||||
- mark_completed:
|
||||
assign:
|
||||
- status: 'COMPLETED'
|
||||
next: 'finish'
|
||||
|
||||
- finish:
|
||||
return: '${status}'
|
||||
Reference in New Issue
Block a user