feat(cloud): ✨ add checkout integration and payment callback flow #38
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: LLM PR Review | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| branches: | |
| - master | |
| workflow_dispatch: | |
| inputs: | |
| pr_number: | |
| description: "Pull request number to review" | |
| required: true | |
| type: number | |
| concurrency: | |
| group: llm-review-${{ github.event.pull_request.number || github.event.inputs.pr_number }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| jobs: | |
| llm-review: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| # This step uses the Anthropic Messages API. | |
| # Secrets required: APIKey; Variables required: APIBase, ModelId | |
| - name: Generate LLM review | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| API_BASE: ${{ vars.ANTHROPIC_BASE_URL }} | |
| API_KEY: ${{ secrets.ANTHROPIC_AUTH_TOKEN }} | |
| MODEL_ID: ${{ vars.ANTHROPIC_MODEL }} | |
| PR_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| set -euo pipefail | |
| IFS=$'\n\t' | |
| pr_json=$(curl -sf \ | |
| -H "Authorization: Bearer $GITHUB_TOKEN" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| "https://api.github.com/repos/$REPO/pulls/$PR_NUMBER") || { | |
| echo "::error::Failed to fetch PR metadata" | |
| exit 1 | |
| } | |
| pr_title=$(printf "%s" "$pr_json" | jq -r '.title // ""') | |
| pr_body=$(printf "%s" "$pr_json" | jq -r '.body // ""') | |
| pr_diff=$(curl -sf \ | |
| -H "Authorization: Bearer $GITHUB_TOKEN" \ | |
| -H "Accept: application/vnd.github.v3.diff" \ | |
| "https://api.github.com/repos/$REPO/pulls/$PR_NUMBER") || { | |
| echo "::error::Failed to fetch PR diff" | |
| exit 1 | |
| } | |
| max_diff_chars=131072 | |
| pr_diff_trimmed="${pr_diff:0:$max_diff_chars}" | |
| # Trim to last complete line to avoid cutting mid-line | |
| pr_diff_trimmed="${pr_diff_trimmed%$'\n'*}" | |
| system_prompt=$(cat <<'SYSPROMPT' | |
| You are a senior software engineer reviewing a pull request for an Electron desktop application (React 18 + TypeScript + Vite + Prisma/SQLite + Ant Design). The codebase follows Clean Architecture with domain/application/infrastructure layers and a three-stage worker pipeline (Split → Convert → Merge) for PDF-to-Markdown conversion via LLM vision APIs. | |
| ## Review priorities (highest to lowest) | |
| 1. **Correctness** — Logic errors, race conditions, broken control flow, incorrect types, off-by-one errors, null/undefined mishandling | |
| 2. **Security** — Command injection, path traversal, credential leaks, unsafe IPC, unvalidated external input (OWASP Top 10) | |
| 3. **Resource & performance** — Memory leaks, unbounded loops, missing cleanup in Electron main process, unnecessary re-renders in React, N+1 queries | |
| 4. **Error handling** — Swallowed errors, overly broad catch blocks, missing user-facing error feedback, silent failures that hide bugs | |
| 5. **API & type contracts** — Breaking changes to IPC channel signatures, mismatched shared types between main/renderer, incorrect Prisma usage | |
| 6. **Maintainability** — Violations of existing project patterns, dead code, missing or misleading abstractions | |
| ## What NOT to comment on | |
| - Pure style/formatting (handled by Prettier + ESLint) | |
| - Adding comments or docstrings to unchanged code | |
| - Minor naming preferences that don't affect clarity | |
| - Suggesting tests for trivial changes | |
| - Hypothetical future improvements unrelated to this PR | |
| ## How to read the diff | |
| The diff uses unified format. Each hunk header looks like: | |
| `@@ -old_start,old_count +new_start,new_count @@ optional_context` | |
| To reference a line: start from `+new_start` and count only context lines (` `) and added lines (`+`), skipping removed lines (`-`). Use the format `file_path:line_number`. | |
| ## Output format | |
| Use this exact structure. **Omit any section that has no items.** Each item must reference a specific location and be actionable. | |
| ### Summary | |
| 1-3 sentences: what this PR does, overall quality assessment, and whether it is ready to merge. | |
| ### Critical | |
| Issues that **must** be fixed before merge — bugs, security vulnerabilities, data loss risks. | |
| - `file:line` — **Title**: Explanation of the issue and a concrete suggestion to fix it. | |
| ### Important | |
| Issues that **should** be fixed — error handling gaps, performance concerns, contract violations. | |
| - `file:line` — **Title**: Explanation and suggestion. | |
| ### Suggestion | |
| Non-blocking improvements — cleaner patterns, minor simplifications, readability tweaks. | |
| - `file:line` — **Title**: Explanation and suggestion. | |
| ### Praise | |
| Highlight 1-3 notably well-done aspects to reinforce good practices. | |
| - `file:line` — **Title**: What was done well and why it matters. | |
| SYSPROMPT | |
| ) | |
| # Write large variables to temp files to avoid ARG_MAX limit | |
| printf "%s" "$system_prompt" > /tmp/system_prompt.txt | |
| printf "%s" "$pr_diff_trimmed" > /tmp/pr_diff.txt | |
| request_body=$(jq -n \ | |
| --arg model "$MODEL_ID" \ | |
| --rawfile system /tmp/system_prompt.txt \ | |
| --arg title "$pr_title" \ | |
| --arg body "$pr_body" \ | |
| --rawfile diff /tmp/pr_diff.txt \ | |
| '{ | |
| model: $model, | |
| max_tokens: 8192, | |
| system: [{ type: "text", text: $system }], | |
| messages: [ | |
| { role: "user", content: ("Review the following pull request.\n\n## Title\n" + $title + "\n\n## Description\n" + $body + "\n\n## Diff\n```diff\n" + $diff + "\n```") } | |
| ] | |
| }') | |
| printf "%s" "$request_body" > /tmp/llm_request.json | |
| http_code=$(curl -s -o /tmp/llm_response.json -w "%{http_code}" \ | |
| --max-time 120 \ | |
| -H "x-api-key: $API_KEY" \ | |
| -H "Content-Type: application/json" \ | |
| -H "anthropic-version: 2023-06-01" \ | |
| "$API_BASE/v1/messages" \ | |
| -d @/tmp/llm_request.json) || { | |
| echo "::error::LLM API request failed (curl error)" | |
| exit 1 | |
| } | |
| if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then | |
| echo "::error::LLM API returned HTTP $http_code" | |
| cat /tmp/llm_response.json | |
| exit 1 | |
| fi | |
| llm_response=$(cat /tmp/llm_response.json) | |
| review_text=$(printf "%s" "$llm_response" | jq -r '[.content[] | select(.type == "text")] | first? | .text // ""') | |
| if [ -z "$review_text" ]; then | |
| echo "::error::LLM response missing content. Raw response:" | |
| printf "%s" "$llm_response" | jq . | |
| exit 1 | |
| fi | |
| review_payload=$(jq -n --arg body "$review_text" '{body: $body, event: "COMMENT"}') | |
| curl -sf --max-time 30 \ | |
| -H "Authorization: Bearer $GITHUB_TOKEN" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| "https://api.github.com/repos/$REPO/pulls/$PR_NUMBER/reviews" \ | |
| -d "$review_payload" >/dev/null || { | |
| echo "::error::Failed to post PR review" | |
| exit 1 | |
| } |