Skip to content

feat(pr-generator-core): add environment config parser, command executor, GitHub R… - #28435

Merged
joneba-google merged 7 commits into
google-gemini:mainfrom
JonE01:pr-generator-utils
Aug 5, 2026
Merged

feat(pr-generator-core): add environment config parser, command executor, GitHub R…#28435
joneba-google merged 7 commits into
google-gemini:mainfrom
JonE01:pr-generator-utils

Conversation

@joneba-google

@joneba-google joneba-google commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

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.

@joneba-google
joneba-google requested a review from a team as a code owner July 17, 2026 19:04
@github-actions github-actions Bot added the size/l A large sized PR label Jul 17, 2026
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown

📊 PR Size: size/L

  • Lines changed: 903
  • Additions: +903
  • Deletions: -0
  • Files changed: 13

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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

  • Package Architecture: Initialized the core workflow package structure for the Gemini CLI Issue-to-PR pipeline.
  • Configuration Management: Implemented a robust configuration parser to handle environment variables, path resolution, and schema validation for Firestore documents.
  • Subprocess Execution: Added a safe command execution utility that captures standard streams and provides structured logging for shell operations.
  • GitHub Integration: Developed a lightweight GitHub REST API client using standard libraries to facilitate automated pull request creation.
  • Preflight Filtering: Introduced an ANSI-sanitization and test-failure analysis module to selectively bypass known sandbox-level test regressions.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread tools/caretaker-agent/cloudrun/pr-generator/workflow/config.py Outdated
Comment thread tools/caretaker-agent/cloudrun/pr-generator/workflow/github_client.py Outdated
Comment thread tools/caretaker-agent/cloudrun/pr-generator/workflow/preflight_filter.py Outdated
Comment thread tools/caretaker-agent/cloudrun/pr-generator/workflow/preflight_filter.py Outdated
@gemini-cli gemini-cli Bot added the status/need-issue Pull requests that need to have an associated issue. label Jul 17, 2026
@joneba-google joneba-google changed the title feat(core): add environment config parser, command executor, GitHub R… feat(pr-generator-core): add environment config parser, command executor, GitHub R… Jul 17, 2026
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)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 adamfweidman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Suggestions

1. Simplification Opportunities

  • command_executor.py: We can simplify subprocess execution by leveraging standard library subprocess.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

:)

@gemini-cli

gemini-cli Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

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.

@adamfweidman adamfweidman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

else:
args.append(token)
else:
args = list(cmd)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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("../").

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the changes and they'll be included in the pr updating this file.

@joneba-google
joneba-google added this pull request to the merge queue Aug 5, 2026
Merged via the queue into google-gemini:main with commit 8b60087 Aug 5, 2026
34 checks passed
@joneba-google
joneba-google deleted the pr-generator-utils branch August 5, 2026 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! size/l A large sized PR status/need-issue Pull requests that need to have an associated issue. status/pr-nudge-sent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants