[codex] Harden DiffScope review operations #62
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: DiffScope Review | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened, review_requested] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| concurrency: | |
| group: diffscope-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| review: | |
| runs-on: ubuntu-latest | |
| if: github.event.pull_request.head.repo.full_name == github.repository && !github.event.pull_request.draft | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| - uses: actions/setup-node@v5 | |
| with: | |
| node-version: 24 | |
| cache: npm | |
| cache-dependency-path: web/package-lock.json | |
| - uses: dtolnay/rust-toolchain@1.88.0 | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Build frontend | |
| working-directory: web | |
| run: | | |
| npm ci | |
| npm run build | |
| - name: Build current DiffScope binary | |
| run: cargo build --release | |
| - name: Get PR diff | |
| run: | | |
| git fetch origin ${{ github.base_ref }} --depth=1 | |
| git diff origin/${{ github.base_ref }}...HEAD > pr.diff | |
| - name: Select review provider | |
| id: provider | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| run: | | |
| if [ -n "${ANTHROPIC_API_KEY}" ]; then | |
| { | |
| echo "configured=true" | |
| echo "model=anthropic/claude-opus-4.5" | |
| echo "adapter=anthropic" | |
| } >> "$GITHUB_OUTPUT" | |
| elif [ -n "${OPENROUTER_API_KEY}" ]; then | |
| { | |
| echo "configured=true" | |
| echo "model=anthropic/claude-opus-4.5" | |
| echo "adapter=openrouter" | |
| } >> "$GITHUB_OUTPUT" | |
| elif [ -n "${OPENAI_API_KEY}" ]; then | |
| { | |
| echo "configured=true" | |
| echo "model=gpt-4o" | |
| echo "adapter=openai" | |
| } >> "$GITHUB_OUTPUT" | |
| else | |
| echo "configured=false" >> "$GITHUB_OUTPUT" | |
| echo "::notice::DiffScope review skipped; no ANTHROPIC_API_KEY, OPENROUTER_API_KEY, or OPENAI_API_KEY secret is configured" | |
| fi | |
| - name: Run DiffScope from this branch | |
| if: steps.provider.outputs.configured == 'true' | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| run: | | |
| ./target/release/diffscope \ | |
| --model "${{ steps.provider.outputs.model }}" \ | |
| --adapter "${{ steps.provider.outputs.adapter }}" \ | |
| --output-format json \ | |
| review --diff pr.diff --output comments.json | |
| - name: Upload review artifact | |
| if: always() && steps.provider.outputs.configured == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: diffscope-review-${{ github.event.pull_request.number }} | |
| path: comments.json | |
| if-no-files-found: ignore | |
| - name: Post comments | |
| if: steps.provider.outputs.configured == 'true' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| if (!fs.existsSync('comments.json')) { | |
| core.notice('DiffScope did not produce comments.json'); | |
| return; | |
| } | |
| const comments = JSON.parse(fs.readFileSync('comments.json', 'utf8')); | |
| const headSha = context.payload.pull_request.head.sha; | |
| const fallback = []; | |
| for (const comment of comments) { | |
| if (!comment.file_path || !comment.line_number || comment.line_number < 1) { | |
| continue; | |
| } | |
| const body = [ | |
| `**${comment.severity}**: ${comment.content}`, | |
| comment.suggestion ? `\nSuggestion: ${comment.suggestion}` : '' | |
| ].join(''); | |
| try { | |
| await github.rest.pulls.createReviewComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.issue.number, | |
| body, | |
| commit_id: headSha, | |
| path: comment.file_path, | |
| line: comment.line_number, | |
| side: "RIGHT" | |
| }); | |
| } catch (error) { | |
| fallback.push(`- **${comment.severity}** ${comment.file_path}:${comment.line_number} ${comment.content}`); | |
| } | |
| } | |
| if (fallback.length > 0) { | |
| const body = [ | |
| "Some review comments could not be placed inline and are listed below:", | |
| "", | |
| ...fallback.slice(0, 100) | |
| ].join("\n"); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body | |
| }); | |
| } |