diff --git a/.github/workflows/automated_pr_review.yaml b/.github/workflows/automated_pr_review.yaml index af2da7e0ca..927adbbd4c 100644 --- a/.github/workflows/automated_pr_review.yaml +++ b/.github/workflows/automated_pr_review.yaml @@ -5,8 +5,6 @@ name: Automated Code Review # to secrets (like GEMINI_API_KEY) even for fork PRs. # Using pull_request for now during setup/testing. on: - pull_request: - types: [opened] issue_comment: types: [created] @@ -15,21 +13,24 @@ permissions: pull-requests: read jobs: + # Always runs so the workflow run exits cleanly without a "No jobs ran" error + # when the review job's if-condition evaluates to false. + noop: + runs-on: ubuntu-latest + steps: + - name: Workflow trigger check + run: echo "Workflow triggered successfully." + review: runs-on: ubuntu-latest - # Trigger only if: - # 1. It is a pull_request event, it is NOT a draft, and the author is a maintainer - # (OWNER, MEMBER, or COLLABORATOR). - # 2. OR it is a regular conversation comment on a pull request, the comment body has a line starting with "/review", - # and the commenter is a maintainer. + # Trigger only if it is a regular comment on a pull request, the comment body has a line + # starting with "/review", and the commenter is a maintainer (OWNER, MEMBER, or COLLABORATOR). if: > - (github.event_name == 'pull_request' && !github.event.pull_request.draft && - contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association)) || - (github.event_name == 'issue_comment' && github.event.issue.pull_request != null && - (startsWith(github.event.comment.body, '/review') || - contains(github.event.comment.body, '\n/review') || - contains(github.event.comment.body, '\r\n/review')) && - contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) + github.event_name == 'issue_comment' && github.event.issue.pull_request != null && + (startsWith(github.event.comment.body, '/review') || + contains(github.event.comment.body, '\n/review') || + contains(github.event.comment.body, '\r\n/review')) && + contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) steps: - name: Checkout PR Branch uses: actions/checkout@v7 @@ -40,6 +41,25 @@ jobs: ref: refs/pull/${{ github.event.pull_request.number || github.event.issue.number }}/head persist-credentials: false + - name: Fetch Base Branch and Negotiate Minimal Diff History + env: + PR_COMMITS: ${{ github.event.pull_request.commits }} + run: | + # 1. Fetch the tip of main + git fetch origin main:refs/remotes/origin/main --depth=1 + + # 2. Check if we already have the merge base (common ancestor) + if ! git merge-base origin/main HEAD >/dev/null 2>&1; then + # If we know the exact number of commits in the PR, deepen by (PR_COMMITS + 10) + if [ -n "$PR_COMMITS" ] && [ "$PR_COMMITS" != "null" ]; then + git fetch --deepen="$((PR_COMMITS + 10))" + fi + # 3. If it's an issue_comment event (where PR_COMMITS is null) or still shallow, unshallow/deepen + if ! git merge-base origin/main HEAD >/dev/null 2>&1; then + git fetch --unshallow || git fetch --deepen=50 + fi + fi + - name: Checkout Reviewbot (Base Branch) uses: actions/checkout@v7 with: diff --git a/tools/private/reviewbot/antigravity_review.py b/tools/private/reviewbot/antigravity_review.py index e415e42a91..3aef480f3b 100644 --- a/tools/private/reviewbot/antigravity_review.py +++ b/tools/private/reviewbot/antigravity_review.py @@ -2,14 +2,16 @@ # requires-python = ">=3.11" # dependencies = [ # "google-antigravity", -# "requests", # ] # /// import argparse import asyncio +import subprocess from pathlib import Path from google.antigravity import Agent, CapabilitiesConfig, LocalAgentConfig +from google.antigravity.models import GeminiAPIEndpoint, ModelTarget +from google.antigravity.types import BuiltinTools def parse_args(): @@ -18,11 +20,26 @@ def parse_args(): return parser.parse_args() +def get_pr_diff() -> str: + """Fetches the git diff for the current pull request against origin/main.""" + try: + return subprocess.check_output( + ["git", "diff", "origin/main...HEAD"], text=True, stderr=subprocess.DEVNULL + ) + except Exception: + return "No diff could be automatically extracted via git commands." + + async def main(): args = parse_args() - # Read prompt file - prompt = Path(args.prompt).read_text() + # Read prompt file and pre-hydrate with the exact PR code diff + base_prompt = Path(args.prompt).read_text() + diff_text = get_pr_diff() + prompt = ( + f"{base_prompt}\n\n## Pull Request Git Diff\n" + f"Here is the exact code diff for this pull request:\n```diff\n{diff_text}\n```" + ) # General coordinator instructions for the reviewer agent. system_instructions = ( @@ -30,15 +47,27 @@ async def main(): "reviews on pull requests." ) + # Create the default endpoint picking up GEMINI_API_KEY from the environment. + endpoint = GeminiAPIEndpoint() + # Initialize the Antigravity Agent in read-only mode for security. # Register the review-pr skill from the local reviewbot folder. + # Provide a comprehensive prioritized cascade across Gemini 3 models + # to automatically fall back if any model hits free-tier quota limits (429) + # or temporary unavailability. config = LocalAgentConfig( + models=[ + ModelTarget(name="gemini-3.5-flash", endpoint=endpoint), + ModelTarget(name="gemini-3.1-pro-preview", endpoint=endpoint), + ModelTarget(name="gemini-3.1-flash-lite", endpoint=endpoint), + ModelTarget(name="gemini-3-pro-preview", endpoint=endpoint), + ModelTarget(name="gemini-flash-latest", endpoint=endpoint), + ModelTarget(name="gemini-pro-latest", endpoint=endpoint), + ], system_instructions=system_instructions, skills_paths=[str(Path(__file__).parent / "skills" / "review-pr" / "SKILL.md")], capabilities=CapabilitiesConfig( - allow_filesystem_read=True, - allow_filesystem_write=False, - allow_network=False, + enabled_tools=BuiltinTools.read_only(), ), ) diff --git a/tools/private/reviewbot/skills/review-pr/SKILL.md b/tools/private/reviewbot/skills/review-pr/SKILL.md index 18c998f9ed..915a526686 100644 --- a/tools/private/reviewbot/skills/review-pr/SKILL.md +++ b/tools/private/reviewbot/skills/review-pr/SKILL.md @@ -5,6 +5,8 @@ description: Perform a read-only code review on a pull request. # review-pr +IMPORTANT: The exact git diff for the pull request is pre-provided right in your prompt. Analyze this provided diff directly in a single pass without calling exploratory directory listing or file reading tools unless you specifically need surrounding lines of context from a modified file. + You are an expert Starlark, Python, and Bazel code reviewer. Analyze the changed files for correctness, edge cases, and performance. Focus strictly on logical correctness, concurrency safety, system architecture, performance bottlenecks,