feat(pr-generator-core): add environment config parser, command executor, GitHub R… - #28435
Conversation
…EST client, and preflight filter
|
📊 PR Size: size/L
|
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request establishes the foundational infrastructure for the Gemini CLI Issue-to-PR Code Generation Pipeline. It introduces modular utilities for managing environment configurations, executing system commands, interacting with the GitHub API, and filtering test outputs. These components are designed to work within a containerized environment, ensuring reliable orchestration of the code generation and evaluation workflow. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces the workflow package for the SSR Agent Orchestrator, adding configuration, command execution, GitHub API integration, and preflight test filtering. The review comments correctly identify several critical issues: a potential remote code execution (RCE) vulnerability from globally trusting workspaces, command injection risks from using shell=True, a missing timeout on GitHub API requests, and brittle regex patterns in the preflight filter that fail to handle monorepo paths and standard Jest/Vitest test summaries.
| cmd_str = " ".join(cmd) if isinstance(cmd, list) else cmd | ||
| logging.info("Executing command: %s (CWD: %s)", cmd_str, active_cwd) | ||
|
|
||
| use_shell = isinstance(cmd, str) |
There was a problem hiding this comment.
Suggestion: We can simplify subprocess execution across the workflow by using standard subprocess.run calls directly (or via a lightweight helper function). Passing argument lists directly without invoking the shell keeps command execution clean, safe, and straightforward:
import subprocess
import logging
def run_cmd(
args: list[str],
cwd: str | None = None,
env: dict[str, str] | None = None,
timeout: float = 300.0,
check: bool = True,
) -> str:
"""Executes a command using standard subprocess without shell invocation."""
try:
result = subprocess.run(
args,
cwd=cwd,
env=env,
capture_output=True,
text=True,
timeout=timeout,
check=check,
)
return result.stdout
except subprocess.CalledProcessError as e:
logging.error("Command '%s' failed (exit %d): %s", " ".join(args), e.returncode, e.stderr)
raise| return False | ||
|
|
||
| # Find total test failure count summary in JEST style output: e.g. "Tests: 3 failed, 4 passed" | ||
| match = _TEST_FAILED_COUNT_RE.search(clean_output) |
There was a problem hiding this comment.
Suggestion: To keep preflight output filtering simple and maintainable, we can strip ANSI codes and check whether test failure lines match an allowed sandbox exception set:
import re
import logging
_ANSI_ESCAPE_RE = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~"])")
ALLOWED_SANDBOX_FAILURES: set[str] = {
"root-privilege-check",
"container-permission-test",
}
def is_preflight_failure_allowed(
test_output: str,
allowed_failures: set[str] = ALLOWED_SANDBOX_FAILURES,
) -> bool:
"""Checks if test failures belong strictly to approved container/sandbox exceptions."""
clean_output = _ANSI_ESCAPE_RE.sub("", test_output)
for line in clean_output.splitlines():
if "FAIL" in line or "FAILED" in line:
if not any(allowed in line for allowed in allowed_failures):
logging.warning("Unapproved preflight test failure detected: %s", line)
return False
return True| self.model_name: str = os.environ.get("MODEL_NAME", "gemini-3.5-flash") | ||
|
|
||
| # Global runtime settings | ||
| self.max_attempts: int = int(os.environ.get("MAX_ATTEMPTS", "5")) |
There was a problem hiding this comment.
Suggestion: We can add a fallback and lower bound using max(..., 1) to ensure max_attempts always evaluates to a positive integer:
try:
self.max_attempts: int = max(int(os.environ.get("MAX_ATTEMPTS", "5")), 1)
except ValueError:
self.max_attempts = 5| title: Title of the Pull Request. | ||
| body: Body description markdown of the Pull Request. | ||
|
|
||
| Returns: |
There was a problem hiding this comment.
Nit: Small docstring update—Returns mentions the HTML URL, but the method returns the PR number as a string:
Returns:
The PR number of the successfully created Pull Request as a string.| "Pull Request created successfully! PR Number: %s", pr_number | ||
| ) | ||
| return pr_number | ||
| except urllib.error.HTTPError as e: |
There was a problem hiding this comment.
Suggestion: We can combine HTTP and network error handling into a single urllib.error.URLError block to keep exception handling concise:
except urllib.error.URLError as e:
err_msg = getattr(e, "reason", e)
logging.error("Failed to create Pull Request: %s", err_msg)
raise GitHubClientError(f"GitHub API Error: {err_msg}") from e
adamfweidman
left a comment
There was a problem hiding this comment.
Code Review Suggestions
1. Simplification Opportunities
command_executor.py: We can simplify subprocess execution by leveraging standard librarysubprocess.run(["cmd", "args..."], check=True)directly. This avoids custom parsing wrappers and keeps execution clean and straightforward.preflight_filter.py: We can streamline test output filtering by stripping ANSI escape sequences and checking if failing test names match the approved sandbox exception set (ALLOWED_SANDBOX_FAILURES).
2. File-Level Suggestions
See inline code suggestions below.
3. Add Tests
:)
|
Hi there! Thank you for your interest in contributing to Gemini CLI. To ensure we maintain high code quality and focus on our prioritized roadmap, we only guarantee review and consideration of pull requests for issues that are explicitly labeled as 'help wanted'. This PR will be closed in 7 days if it remains without that designation. We encourage you to find and contribute to existing 'help wanted' issues in our backlog! Thank you for your understanding. |
| else: | ||
| args.append(token) | ||
| else: | ||
| args = list(cmd) |
There was a problem hiding this comment.
If cmd is empty or contains only environment variable assignments (e.g., "FOO=bar"), args evaluates to []. Executing subprocess.run([]) raises an IndexError. Consider adding a validation check to ensure args is non-empty before calling subprocess.run.
There was a problem hiding this comment.
I also made this change for a follow-up pr.
| raw_str = str(path).replace("\x00", "").strip() | ||
| if not raw_str: | ||
| return None | ||
| clean_path = os.path.normpath(raw_str) |
There was a problem hiding this comment.
In sanitize_relative_path, os.path.normpath does not normalize backslashes into forward slashes on POSIX systems. Additionally, clean_path.startswith(".. ") flags safe relative filenames like ..note.txt. Consider normalizing backslashes (path.replace("\\", "/")) prior to normpath, and checking for clean_path == ".." or clean_path.startswith("../").
There was a problem hiding this comment.
I made the changes and they'll be included in the pr updating this file.
Overview
This PR introduces the foundational utility modules and package structure for the Gemini CLI SSR Pipeline. It packages configuration parsing, subprocess execution with structured logging, GitHub v3 REST API client integration, and ANSI preflight test output filtering.
──────
Summary of Changes
1. Package Initialization (init.py)
• Establishes the workflow package definition and documents the core module architecture.
2. Environment Configuration (config.py)
• Environment Parser (config.py): Parses and validates required GCP environment variables (REPO_URL, GIT_TOKEN, FIRESTORE_DOC, FIRESTORE_ID, EXECUTION_ID, GOOGLE_CLOUD_PROJECT, MAX_ATTEMPTS).
• Path Resolution: Dynamically configures temporary PR and evaluation workspace paths (/tmp/pr/, /tmp/eval/).
• Schema Validation (config.py): Ensures FIRESTORE_DOC contains valid JSON and raises structured config.py on failure.
3. Subprocess Execution Utility (command_executor.py)
• Safe Command Runner (command_executor.py): Executes system shell commands, logs execution parameters and working directories, and cleanly captures standard streams.
• Error Handling: Raises command_executor.py with exit codes, stdout, and stderr on command failures.
4. GitHub REST API Client (github_client.py)
• Lightweight REST Integration (github_client.py): Uses standard urllib to avoid heavy external HTTP client dependencies in the container.
• Automated PR Submission (github_client.py): Authenticates using Bearer tokens and submits POST requests to GitHub v3 REST API endpoints (/repos/{owner}/{repo}/pulls), returning the created PR URL.
5. Preflight Test & Lint Output Filter (preflight_filter.py)
• ANSI Sanitization (preflight_filter.py): Removes terminal escape sequences using a module-level precompiled regex constant (_ANSI_ESCAPE_RE).
• Failure Analysis (preflight_filter.py): Analyzes test output traces to selectively bypass known sandbox-level root privilege test failures while flagging unapproved regressions.