Update ai-review.yml #2
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: ExpTechAI | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| issue_comment: | |
| types: [created] | |
| jobs: | |
| ai-review: | |
| if: | | |
| github.event_name == 'pull_request' || | |
| ( | |
| github.event_name == 'issue_comment' && | |
| github.event.issue.pull_request && | |
| ( | |
| contains(github.event.comment.body, '/review') || | |
| contains(github.event.comment.body, '@ExpTechAI') | |
| ) && | |
| ( | |
| github.event.comment.author_association == 'OWNER' || | |
| github.event.comment.author_association == 'MEMBER' || | |
| github.event.comment.author_association == 'COLLABORATOR' | |
| ) | |
| ) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| steps: | |
| - name: Get PR ref | |
| id: pr | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.PAT_TOKEN }} | |
| script: | | |
| if (context.eventName === 'pull_request') { | |
| return { | |
| sha: context.payload.pull_request.head.sha, | |
| number: context.payload.pull_request.number | |
| }; | |
| } | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.payload.issue.number | |
| }); | |
| return { sha: pr.head.sha, number: pr.number }; | |
| - name: React to comment | |
| if: github.event_name == 'issue_comment' | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.PAT_TOKEN }} | |
| script: | | |
| await github.rest.reactions.createForIssueComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: context.payload.comment.id, | |
| content: 'eyes' | |
| }); | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ fromJSON(steps.pr.outputs.result).sha }} | |
| fetch-depth: 0 | |
| - name: Run AI Code Review | |
| id: review | |
| uses: zxcloli666/AI-Code-Review@v1.3.0 | |
| env: | |
| PR_NUMBER: ${{ fromJSON(steps.pr.outputs.result).number }} | |
| with: | |
| GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} | |
| OPENAI_API_KEY: ${{ secrets.VLLM_API_KEY }} | |
| OPENAI_API_BASE_URL: ${{ secrets.VLLM_BASE_URL }} | |
| OPENAI_API_MODEL: "auto" | |
| REVIEW_LANGUAGE: "zh" | |
| MAX_CHUNK_SIZE: "60000" | |
| SEVERITY_THRESHOLD: "error" | |
| SILENT_MODE: "false" | |
| ENABLE_LINTERS: "true" | |
| ENABLE_AST: "true" | |
| ENABLE_DEPENDENCY_ANALYSIS: "true" | |
| - name: Enforce strict review | |
| if: always() | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.PAT_TOKEN }} | |
| script: | | |
| const { owner, repo } = context.repo; | |
| const pr_number = ${{ fromJSON(steps.pr.outputs.result).number }}; | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner, repo, issue_number: pr_number, per_page: 100 | |
| }); | |
| const aiComments = comments | |
| .filter(c => c.user.type === 'Bot' || c.user.login.includes('github-actions')) | |
| .filter(c => c.body.includes('AI CODE REVIEW') || c.body.includes('AI Code Review')) | |
| .sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); | |
| if (aiComments.length === 0) { | |
| core.setFailed('❌ 找不到 AI Review 結果留言 —— AI 審查可能沒跑完或 vLLM 連線失敗'); | |
| return; | |
| } | |
| const latest = aiComments[0].body; | |
| core.info('=== 最新 AI Review 留言節錄 ==='); | |
| core.info(latest.slice(0, 500)); | |
| const blockers = []; | |
| if (/❌\s*REJECTED|❌\s*BLOCKED|Verdict.*REJECTED|verdict.*reject/i.test(latest)) { | |
| blockers.push('AI 給出 REJECTED 判定'); | |
| } | |
| const critMatch = latest.match(/Critical[^0-9]*?(\d+)/i); | |
| if (critMatch && parseInt(critMatch[1]) > 0) { | |
| blockers.push(`發現 ${critMatch[1]} 個 Critical 問題`); | |
| } | |
| const scoreMatch = latest.match(/Quality Score[^\d]*(\d+)\s*\/\s*100/i); | |
| if (scoreMatch && parseInt(scoreMatch[1]) < 60) { | |
| blockers.push(`Quality Score 過低: ${scoreMatch[1]}/100(門檻 60)`); | |
| } | |
| if (blockers.length > 0) { | |
| const msg = '🚫 嚴格模式擋 PR:\n' + blockers.map(b => ` - ${b}`).join('\n'); | |
| core.setFailed(msg); | |
| await github.rest.issues.createComment({ | |
| owner, repo, issue_number: pr_number, | |
| body: `## 🚫 嚴格模式:此 PR 被擋下\n\n${blockers.map(b => `- ${b}`).join('\n')}\n\n請依照上方 AI Review 建議修正後重新 push。` | |
| }); | |
| } else { | |
| core.info('✅ 嚴格模式通過,沒有發現 blocker'); | |
| } |