-
Notifications
You must be signed in to change notification settings - Fork 78
pr pre-flight, github action version #5558
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 14 commits
1a50f38
37a731c
22cb219
3644583
71b2dc7
02460be
4137af0
29f6006
0f1cbdb
9a1badf
647f39f
bfed112
bb2ccbb
540bffd
9439709
da4516a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,187 @@ | ||||||||||
| # SPDX-FileCopyrightText: Copyright (c) 2023-present NVIDIA CORPORATION & AFFILIATES. | ||||||||||
| # All rights reserved. | ||||||||||
| # SPDX-License-Identifier: BSD-3-Clause | ||||||||||
|
|
||||||||||
| name: Claude CLI PR Review | ||||||||||
| on: | ||||||||||
| pull_request: | ||||||||||
| types: [opened, synchronize, ready_for_review] | ||||||||||
|
|
||||||||||
| concurrency: | ||||||||||
| group: ${{ github.workflow }}-${{ github.event.pull_request.number }} | ||||||||||
| cancel-in-progress: true | ||||||||||
|
|
||||||||||
| run-name: Claude review for PR ${{ github.event.pull_request.number }} - ${{ github.event.pull_request.head.sha }} | ||||||||||
|
|
||||||||||
| jobs: | ||||||||||
| claude-code-review: | ||||||||||
| name: Run Claude Code Review | ||||||||||
| # Skip if PR is in draft | ||||||||||
| if: github.event.pull_request.draft == false | ||||||||||
| runs-on: ubuntu-latest | ||||||||||
| permissions: | ||||||||||
| pull-requests: write | ||||||||||
| contents: read | ||||||||||
| env: | ||||||||||
| CLAUDE_OUTPUT_DIR: artifacts/claude_review/${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }} | ||||||||||
|
|
||||||||||
| steps: | ||||||||||
| - name: Checkout code | ||||||||||
| uses: actions/checkout@v4 | ||||||||||
| with: | ||||||||||
| fetch-depth: 0 | ||||||||||
|
|
||||||||||
| - name: Setup Node.js | ||||||||||
| uses: actions/setup-node@v4 | ||||||||||
| with: | ||||||||||
| node-version: '20' | ||||||||||
|
|
||||||||||
| - name: Set up Python | ||||||||||
| uses: actions/setup-python@v4 | ||||||||||
| with: | ||||||||||
| python-version: '3.10' | ||||||||||
|
|
||||||||||
| - name: Install npm dependencies | ||||||||||
| run: | | ||||||||||
| npm install -g @anthropic-ai/claude-code @musistudio/claude-code-router@1.0.72 | ||||||||||
| echo "$(npm config get prefix)/bin" >> $GITHUB_PATH | ||||||||||
|
|
||||||||||
| - name: Setup and start Claude Code Router | ||||||||||
| env: | ||||||||||
| NIM_KEY: ${{ secrets.NIM_KEY }} | ||||||||||
| run: | | ||||||||||
| mkdir -p $HOME/.claude-code-router | ||||||||||
| cat <<EOF > $HOME/.claude-code-router/config.json | ||||||||||
| { | ||||||||||
| "LOG": true, | ||||||||||
| "API_TIMEOUT_MS": 60000, | ||||||||||
| "NON_INTERACTIVE_MODE": true, | ||||||||||
| "Providers": [ | ||||||||||
| { | ||||||||||
| "name": "nim", | ||||||||||
| "api_base_url": "https://integrate.api.nvidia.com/v1/chat/completions", | ||||||||||
| "api_key": "\$NIM_KEY", | ||||||||||
| "models": [ | ||||||||||
| "moonshotai/kimi-k2-thinking", | ||||||||||
| "minimaxai/minimax-m2" | ||||||||||
| ], | ||||||||||
| "transformer": { | ||||||||||
| "use": [] | ||||||||||
| } | ||||||||||
| } | ||||||||||
| ], | ||||||||||
| "Router": { | ||||||||||
| "default": "nim,moonshotai/kimi-k2-thinking" | ||||||||||
| }, | ||||||||||
| "transformers": [] | ||||||||||
| } | ||||||||||
| EOF | ||||||||||
| nohup ccr restart & | ||||||||||
xwang233 marked this conversation as resolved.
Show resolved
Hide resolved
xwang233 marked this conversation as resolved.
Show resolved
Hide resolved
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic:
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: use
Suggested change
|
||||||||||
| sleep 5 | ||||||||||
| shell: bash | ||||||||||
|
|
||||||||||
| - name: Run Claude Code via wrapper | ||||||||||
| env: | ||||||||||
| ANTHROPIC_AUTH_TOKEN: 'test' | ||||||||||
| ANTHROPIC_API_KEY: '' | ||||||||||
| ANTHROPIC_BASE_URL: http://localhost:3456 | ||||||||||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||||||||||
| PR_SHA: ${{ github.event.pull_request.head.sha }} | ||||||||||
| PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} | ||||||||||
| PR_BASE_REF: ${{ github.event.pull_request.base.ref }} | ||||||||||
| PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} | ||||||||||
xwang233 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
| PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} | ||||||||||
| run: | | ||||||||||
| python -m tools.pr_preflight_launcher --ai-backend claude --output-dir "${CLAUDE_OUTPUT_DIR}" | ||||||||||
|
|
||||||||||
| - name: Print Claude error (if any) | ||||||||||
| if: always() | ||||||||||
| run: | | ||||||||||
| if [ -f "${{ env.CLAUDE_OUTPUT_DIR }}/error.txt" ]; then | ||||||||||
| echo "===== Claude error.txt =====" | ||||||||||
| sed -n '1,200p' "${{ env.CLAUDE_OUTPUT_DIR }}/error.txt" | ||||||||||
| else | ||||||||||
| echo "No error.txt found in ${{ env.CLAUDE_OUTPUT_DIR }}" | ||||||||||
| fi | ||||||||||
|
|
||||||||||
| - name: Upload Claude review artifacts | ||||||||||
| if: always() | ||||||||||
| uses: actions/upload-artifact@v4 | ||||||||||
| with: | ||||||||||
| name: claude-review-${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }} | ||||||||||
| path: ${{ env.CLAUDE_OUTPUT_DIR }}/** | ||||||||||
|
|
||||||||||
| - name: Post Claude review to PR | ||||||||||
| if: always() | ||||||||||
| uses: actions/github-script@v7 | ||||||||||
| with: | ||||||||||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||||||||||
| script: | | ||||||||||
| const fs = require('fs'); | ||||||||||
| const path = require('path'); | ||||||||||
|
|
||||||||||
| const outputDir = process.env.CLAUDE_OUTPUT_DIR; | ||||||||||
| const commentMarker = '<!-- claude-pr-review-bot -->'; | ||||||||||
|
|
||||||||||
| // Check if review errored or has no output | ||||||||||
| const errorFile = path.join(outputDir, 'error.txt'); | ||||||||||
| const successFile = path.join(outputDir, 'success_raw_output.txt'); | ||||||||||
|
|
||||||||||
| // Skip posting if error occurred | ||||||||||
| if (fs.existsSync(errorFile)) { | ||||||||||
| console.log('Review encountered an error. Skipping comment post.'); | ||||||||||
| return; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Skip if no success output | ||||||||||
| if (!fs.existsSync(successFile)) { | ||||||||||
| console.log('No review output found. Skipping comment post.'); | ||||||||||
| return; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Note: We post even if verdict is FAILED because that helps | ||||||||||
| // PR developers address issues and improve their code | ||||||||||
|
|
||||||||||
| // Read review output | ||||||||||
| const reviewContent = fs.readFileSync(successFile, 'utf8'); | ||||||||||
|
|
||||||||||
| // Prepare comment body | ||||||||||
| const commentBody = `${commentMarker} | ||||||||||
| ## 🤖 Claude PR Review | ||||||||||
|
|
||||||||||
| **Commit:** ${context.payload.pull_request.head.sha} | ||||||||||
|
|
||||||||||
| ${reviewContent} | ||||||||||
|
|
||||||||||
| --- | ||||||||||
| *Review generated at ${new Date().toISOString()}*`; | ||||||||||
|
|
||||||||||
| // Find existing comment | ||||||||||
| const { data: comments } = await github.rest.issues.listComments({ | ||||||||||
| owner: context.repo.owner, | ||||||||||
| repo: context.repo.repo, | ||||||||||
| issue_number: context.payload.pull_request.number, | ||||||||||
| }); | ||||||||||
|
|
||||||||||
| const existingComment = comments.find(comment => | ||||||||||
| comment.body && comment.body.includes(commentMarker) | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| // Create or update comment | ||||||||||
| if (existingComment) { | ||||||||||
| await github.rest.issues.updateComment({ | ||||||||||
| owner: context.repo.owner, | ||||||||||
| repo: context.repo.repo, | ||||||||||
| comment_id: existingComment.id, | ||||||||||
| body: commentBody, | ||||||||||
| }); | ||||||||||
| console.log('Updated existing PR comment'); | ||||||||||
| } else { | ||||||||||
| await github.rest.issues.createComment({ | ||||||||||
| owner: context.repo.owner, | ||||||||||
| repo: context.repo.repo, | ||||||||||
| issue_number: context.payload.pull_request.number, | ||||||||||
| body: commentBody, | ||||||||||
| }); | ||||||||||
| console.log('Created new PR comment'); | ||||||||||
| } | ||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,165 @@ | ||||||||||
| name: Gemini CLI PR Review | ||||||||||
| on: | ||||||||||
| pull_request: | ||||||||||
| types: [opened, synchronize, ready_for_review] | ||||||||||
|
|
||||||||||
| concurrency: | ||||||||||
| group: ${{ github.workflow }}-${{ github.event.pull_request.number }} | ||||||||||
| cancel-in-progress: true | ||||||||||
|
|
||||||||||
| run-name: Gemini review for PR ${{ github.event.pull_request.number }} - ${{ github.event.pull_request.head.sha }} | ||||||||||
|
|
||||||||||
| jobs: | ||||||||||
| gemini-pr-review: | ||||||||||
| # Temporarily disabled to avoid burning API tokens | ||||||||||
| if: false | ||||||||||
| # Skip if PR is in draft | ||||||||||
| # if: github.event.pull_request.draft == false | ||||||||||
| runs-on: ubuntu-latest | ||||||||||
| permissions: | ||||||||||
| contents: read | ||||||||||
| pull-requests: write | ||||||||||
| env: | ||||||||||
| GEMINI_OUTPUT_DIR: artifacts/gemini_review/${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }} | ||||||||||
| steps: | ||||||||||
| - name: Checkout code | ||||||||||
| uses: actions/checkout@v4 | ||||||||||
| with: | ||||||||||
| fetch-depth: 0 | ||||||||||
|
|
||||||||||
| - name: Setup Node.js | ||||||||||
| uses: actions/setup-node@v4 | ||||||||||
| with: | ||||||||||
| node-version: '20' | ||||||||||
|
|
||||||||||
| - name: Install Gemini CLI | ||||||||||
| shell: bash | ||||||||||
| run: | | ||||||||||
| npm install -g @google/gemini-cli@latest | ||||||||||
| echo "$(npm config get prefix)/bin" >> $GITHUB_PATH | ||||||||||
|
|
||||||||||
| - name: Verify Gemini CLI | ||||||||||
| shell: bash | ||||||||||
| run: | | ||||||||||
| which gemini | ||||||||||
| gemini --version | ||||||||||
|
|
||||||||||
| - name: Set up Python | ||||||||||
| uses: actions/setup-python@v4 | ||||||||||
| with: | ||||||||||
| python-version: '3.10' | ||||||||||
|
|
||||||||||
| - name: Install/Upgrade Google Python Client | ||||||||||
| shell: bash | ||||||||||
| run: | | ||||||||||
| python -m pip install --upgrade pip | ||||||||||
| pip install --upgrade google-generativeai | ||||||||||
| # If you have a requirements.txt file, you might use this instead: | ||||||||||
| # pip install -r requirements.txt --upgrade | ||||||||||
|
|
||||||||||
| - name: Run Gemini PR review | ||||||||||
| env: | ||||||||||
| #GEMINI_MODEL: gemini-1.5-flash-latest | ||||||||||
| GEMINI_API_KEY: ${{ secrets.GEMINI_TEST }} | ||||||||||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||||||||||
| PR_SHA: ${{ github.event.pull_request.head.sha }} | ||||||||||
| PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} | ||||||||||
| PR_BASE_REF: ${{ github.event.pull_request.base.ref }} | ||||||||||
| PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} | ||||||||||
xwang233 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
| PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} | ||||||||||
| run: | | ||||||||||
| python -m tools.pr_preflight_launcher --ai-backend gemini --output-dir "${GEMINI_OUTPUT_DIR}" | ||||||||||
|
|
||||||||||
| - name: Print Gemini error (if any) | ||||||||||
| if: always() | ||||||||||
| run: | | ||||||||||
| if [ -f "${{ env.GEMINI_OUTPUT_DIR }}/error.txt" ]; then | ||||||||||
| echo "===== Gemini error.txt =====" | ||||||||||
| sed -n '1,200p' "${{ env.GEMINI_OUTPUT_DIR }}/error.txt" | ||||||||||
| else | ||||||||||
| echo "No error.txt found in ${{ env.GEMINI_OUTPUT_DIR }}" | ||||||||||
| fi | ||||||||||
|
|
||||||||||
| - name: Post Gemini review to PR | ||||||||||
| if: always() | ||||||||||
| uses: actions/github-script@v7 | ||||||||||
| with: | ||||||||||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||||||||||
| script: | | ||||||||||
| const fs = require('fs'); | ||||||||||
| const path = require('path'); | ||||||||||
|
|
||||||||||
| const outputDir = process.env.GEMINI_OUTPUT_DIR; | ||||||||||
| const commentMarker = '<!-- gemini-pr-review-bot -->'; | ||||||||||
|
|
||||||||||
| // Check if review failed or errored - if so, don't post comment | ||||||||||
| const errorFile = path.join(outputDir, 'error.txt'); | ||||||||||
| const verdictFile = path.join(outputDir, 'review_verdict.txt'); | ||||||||||
| const successFile = path.join(outputDir, 'success_raw_output.txt'); | ||||||||||
|
|
||||||||||
| // Skip posting if error occurred | ||||||||||
| if (fs.existsSync(errorFile)) { | ||||||||||
| console.log('Review encountered an error. Skipping comment post.'); | ||||||||||
| return; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Skip posting if review failed | ||||||||||
| if (fs.existsSync(verdictFile)) { | ||||||||||
| const verdict = fs.readFileSync(verdictFile, 'utf8').trim(); | ||||||||||
| if (verdict.includes('FAILED')) { | ||||||||||
| console.log('Review verdict: FAILED. Skipping comment post.'); | ||||||||||
| return; | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Skip if no success output | ||||||||||
| if (!fs.existsSync(successFile)) { | ||||||||||
| console.log('No review output found. Skipping comment post.'); | ||||||||||
| return; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Read review output | ||||||||||
| const reviewContent = fs.readFileSync(successFile, 'utf8'); | ||||||||||
|
|
||||||||||
| // Prepare comment body | ||||||||||
| const commentBody = `${commentMarker} | ||||||||||
| ## 🤖 Gemini PR Review | ||||||||||
|
|
||||||||||
| **Commit:** ${context.payload.pull_request.head.sha} | ||||||||||
|
|
||||||||||
| ${reviewContent} | ||||||||||
|
|
||||||||||
| --- | ||||||||||
| *Review generated at ${new Date().toISOString()}*`; | ||||||||||
|
|
||||||||||
| // Find existing comment | ||||||||||
| const { data: comments } = await github.rest.issues.listComments({ | ||||||||||
| owner: context.repo.owner, | ||||||||||
| repo: context.repo.repo, | ||||||||||
| issue_number: context.payload.pull_request.number, | ||||||||||
| }); | ||||||||||
|
|
||||||||||
| const existingComment = comments.find(comment => | ||||||||||
| comment.body && comment.body.includes(commentMarker) | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| // Create or update comment | ||||||||||
| if (existingComment) { | ||||||||||
| await github.rest.issues.updateComment({ | ||||||||||
| owner: context.repo.owner, | ||||||||||
| repo: context.repo.repo, | ||||||||||
| comment_id: existingComment.id, | ||||||||||
| body: commentBody, | ||||||||||
| }); | ||||||||||
| console.log('Updated existing PR comment'); | ||||||||||
| } else { | ||||||||||
| await github.rest.issues.createComment({ | ||||||||||
| owner: context.repo.owner, | ||||||||||
| repo: context.repo.repo, | ||||||||||
| issue_number: context.payload.pull_request.number, | ||||||||||
| body: commentBody, | ||||||||||
| }); | ||||||||||
| console.log('Created new PR comment'); | ||||||||||
| } | ||||||||||
|
|
||||||||||
|
|
||||||||||
xwang233 marked this conversation as resolved.
Show resolved
Hide resolved
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. syntax: file ends with blank lines after closing brace - syntax error leaves JavaScript incomplete
Suggested change
Comment on lines
+163
to
+165
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. syntax: trailing blank lines after closing brace - the github-script block's JavaScript is incomplete and will cause syntax errors when the workflow runs
Suggested change
|
||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "action": "opened", | ||
| "pull_request": { | ||
| "draft": false, | ||
| "number": 5489, | ||
| "head": { "sha": "9f9a6b2f91519b3dc02fe7ec7a5f2a3b98398338", "ref": "feature/branch" }, | ||
| "base": { "sha": "f8b8551a720cd5c3a9aa8950e5a50fb7d420cbe5", "ref": "main" } | ||
| } | ||
| } | ||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # start from the official, clean Node.js 20 image (Debian-based) | ||
| # Gemini seems to go haywire with older node versions (e.g. 18) | ||
| FROM node:20 | ||
|
|
||
| COPY pr_preflight_launcher.py /usr/local/bin/ | ||
| COPY utils.py /usr/local/bin/tools/ | ||
| COPY git_helpers.py /usr/local/bin/tools/ | ||
| COPY ai_cli_wrapper.py /usr/local/bin/tools/ | ||
mattwalsh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| # Add Python 3 and vim | ||
| RUN apt-get update && apt-get install -y python3 python3-pip vim | ||
|
|
||
| # Install the latest Gemini CLI globally inside the container | ||
| RUN npm install -g @google/gemini-cli --no-update-notifier | ||
|
|
||
| RUN node -v | ||
| RUN python3 --version | ||
| RUN gemini --version | ||
|
|
||
| # 5. Set a working directory (good practice) | ||
| WORKDIR /app | ||
|
|
||
| # 6. Set the default command. When the container starts, | ||
| # it will just run "bash", giving you an interactive shell. | ||
| CMD ["/bin/bash"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| # run me from the 'ai_cli_docker_test' directory | ||
|
|
||
| (cd .. && docker build -f ai_cli_docker_test/Dockerfile -t ai_cli_docker_test_shell .) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: missing
fetch-depth: 0- without full git history,git merge-baseinpr_preflight_launcher.py:85will fail when computing the merge base between branches