From 250fe2b9dc5d449c2aa0cbe7626173145272b583 Mon Sep 17 00:00:00 2001 From: "Anaz S. Aji" Date: Mon, 8 Jun 2026 09:47:50 +0700 Subject: [PATCH 1/2] fix: harden cora-review CI download + improve review prompt consistency --- .github/actions/cora-review/action.yml | 58 ++++++++++++++++++++++---- src/engine/llm.rs | 22 ++++++---- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/.github/actions/cora-review/action.yml b/.github/actions/cora-review/action.yml index 1770479..c199ec7 100644 --- a/.github/actions/cora-review/action.yml +++ b/.github/actions/cora-review/action.yml @@ -75,25 +75,65 @@ runs: echo "Unsupported architecture: $ARCH" exit 1 fi - curl -sL "https://github.com/codecoradev/cora-cli/releases/download/${CORA_VERSION}/${ASSET}-${CORA_VERSION}.tar.gz" \ - -o /tmp/cora.tar.gz - curl -sL "https://github.com/codecoradev/cora-cli/releases/download/${CORA_VERSION}/checksums-sha256.txt" \ - -o /tmp/cora-checksums.txt || true + + DOWNLOAD_URL="https://github.com/codecoradev/cora-cli/releases/download/${CORA_VERSION}/${ASSET}-${CORA_VERSION}.tar.gz" + CHECKSUM_URL="https://github.com/codecoradev/cora-cli/releases/download/${CORA_VERSION}/checksums-sha256.txt" + + # Download tarball with retry + exponential backoff + # Handles: CDN propagation delay on fresh releases, transient network errors, rate limits + for i in 1 2 3 4 5; do + rm -f /tmp/cora.tar.gz + HTTP_CODE=$(curl --fail --show-error -sL --connect-timeout 15 --max-time 120 \ + -w '%{http_code}' -o /tmp/cora.tar.gz "$DOWNLOAD_URL" || echo "000") + FILE_SIZE=$(wc -c < /tmp/cora.tar.gz 2>/dev/null || echo 0) + echo "Download attempt $i: HTTP $HTTP_CODE, size ${FILE_SIZE} bytes" + if [ "$HTTP_CODE" = "200" ] && [ "$FILE_SIZE" -gt 1000 ]; then + break + fi + if [ "$i" -eq 5 ]; then + echo "::error::Failed to download ${ASSET}-${CORA_VERSION}.tar.gz after 5 attempts (last HTTP $HTTP_CODE, ${FILE_SIZE} bytes)" + exit 1 + fi + BACKOFF=$((i * 10)) + echo "Attempt $i failed (HTTP $HTTP_CODE), retrying in ${BACKOFF}s..." + sleep "$BACKOFF" + done + + # Validate downloaded file is actual gzip + FILE_TYPE=$(file -b /tmp/cora.tar.gz 2>/dev/null || echo "unknown") + if ! echo "$FILE_TYPE" | grep -qi "gzip"; then + echo "::error::Downloaded file is not gzip. Got: $FILE_TYPE (HTTP $HTTP_CODE, ${FILE_SIZE} bytes)" + exit 1 + fi + + # Download and verify checksums + CHECKSUM_HTTP=$(curl --fail --show-error -sL --connect-timeout 15 --max-time 30 \ + -w '%{http_code}' -o /tmp/cora-checksums.txt "$CHECKSUM_URL" || echo "000") if [ -f /tmp/cora-checksums.txt ] && [ -s /tmp/cora-checksums.txt ]; then - EXPECTED=$(grep "${ASSET}-${CORA_VERSION}.tar.gz" /tmp/cora-checksums.txt | awk '{print $1}' || true) + EXPECTED=$(awk -v name="${ASSET}-${CORA_VERSION}.tar.gz" '$2 == name {print $1}' /tmp/cora-checksums.txt) ACTUAL=$(sha256sum /tmp/cora.tar.gz | awk '{print $1}') - if [ -n "$EXPECTED" ] && [ "$EXPECTED" != "$ACTUAL" ]; then + if [ -z "$EXPECTED" ]; then + echo "::error::No checksum entry for ${ASSET}-${CORA_VERSION}.tar.gz — possible release asset mismatch" + echo "Checksum file contents:" + cat /tmp/cora-checksums.txt + rm -f /tmp/cora-checksums.txt + exit 1 + fi + if [ "$EXPECTED" != "$ACTUAL" ]; then echo "::error::Checksum verification failed for ${ASSET}-${CORA_VERSION}.tar.gz" echo "Expected: $EXPECTED" echo "Actual: $ACTUAL" + rm -f /tmp/cora-checksums.txt exit 1 - elif [ -z "$EXPECTED" ]; then - echo "::warning::No checksum found for ${ASSET}-${CORA_VERSION}.tar.gz in checksums file" fi + echo "Checksum verified OK" rm -f /tmp/cora-checksums.txt else - echo "::warning::No checksums file found, skipping verification" + echo "::error::Failed to download checksums file (HTTP $CHECKSUM_HTTP) — cannot verify binary integrity" + rm -f /tmp/cora-checksums.txt + exit 1 fi + tar xzf /tmp/cora.tar.gz -C /usr/local/bin cora rm -f /tmp/cora.tar.gz chmod +x /usr/local/bin/cora diff --git a/src/engine/llm.rs b/src/engine/llm.rs index 51af3e7..b08022a 100644 --- a/src/engine/llm.rs +++ b/src/engine/llm.rs @@ -90,24 +90,28 @@ struct Usage { } /// System prompt for code review. -const REVIEW_SYSTEM_PROMPT: &str = r#"You are an expert code reviewer providing actionable feedback on code diffs. +const REVIEW_SYSTEM_PROMPT: &str = r#"You are an expert code reviewer providing thorough, actionable feedback on code diffs. CRITICAL CONSTRAINTS: 1. You MUST ONLY comment on files that appear in the diff. Do NOT invent or hallucinate file paths. 2. Each issue MUST have a clear, descriptive title (one brief sentence, max 100 chars). -3. If uncertain whether something is a real issue, omit it rather than guessing. +3. Report any issue where you can point to SPECIFIC CODE in the diff that is wrong or risky. + Do NOT report speculative concerns without concrete evidence from the diff. + When in doubt, downgrade severity rather than omitting — a borderline concern is a valid minor/info finding. +4. Common patterns to always check: unvalidated inputs, missing error handling, resource leaks, race conditions, off-by-one errors, unchecked edge cases. SEVERITY LEVELS: - "critical": Security vulnerabilities, crashes, data loss, breaking bugs -- "major": Bugs that affect functionality, significant problems -- "minor": Style issues, small nitpicks, minor improvements +- "major": Bugs that affect functionality, logic errors, missing error handling, significant problems +- "minor": Style issues, small nitpicks, minor improvements, borderline concerns backed by evidence - "info": Suggestions, optional enhancements FOCUS AREAS (in priority order): -1. Security vulnerabilities (SQL injection, XSS, auth issues, data exposure) -2. Bugs and logic errors (off-by-one, null handling, race conditions) -3. Performance problems (inefficient algorithms, memory leaks, N+1 queries) -4. Best practices (idiomatic code, error handling, naming) +1. Security vulnerabilities (SQL injection, XSS, auth issues, data exposure, unsafe deserialization) +2. Bugs and logic errors (off-by-one, null handling, race conditions, incorrect conditions, missing edge cases) +3. Error handling (unchecked results, swallowed errors, missing cleanup on failure paths) +4. Performance problems (inefficient algorithms, memory leaks, N+1 queries, unnecessary allocations) +5. Best practices (idiomatic code, naming, DRY, separation of concerns) RESPONSE FORMAT: Return a JSON array of objects with these fields: @@ -116,7 +120,7 @@ Return a JSON array of objects with these fields: - "severity": "critical" | "major" | "minor" | "info" - "issue_type": string — category (security, performance, bugs, best_practice, style, suggestion) - "title": string — short description (max 100 chars) -- "body": string — detailed explanation +- "body": string — detailed explanation with specific code reference - "suggested_fix": string or null — optional fix suggestion If no issues are found, return: [] From 0edc07c130664762bb59a6221b89e8e3034fdaf6 Mon Sep 17 00:00:00 2001 From: "Anaz S. Aji" Date: Mon, 8 Jun 2026 10:09:16 +0700 Subject: [PATCH 2/2] chore: remove cora-review-simple + upgrade docs examples - Remove unused cora-review-simple action (not referenced by any workflow) - Upgrade docs/examples.md with comprehensive GitHub Actions section - Add reusable action reference (codecoradev/cora-cli/.github/actions/cora-review@v0.4.6) - Document all action inputs, options, and custom configuration - Add minimal setup alternative for users who prefer direct install --- .github/actions/cora-review-simple/action.yml | 231 ------------------ docs/examples.md | 110 ++++++++- 2 files changed, 103 insertions(+), 238 deletions(-) delete mode 100644 .github/actions/cora-review-simple/action.yml diff --git a/.github/actions/cora-review-simple/action.yml b/.github/actions/cora-review-simple/action.yml deleted file mode 100644 index c217dcb..0000000 --- a/.github/actions/cora-review-simple/action.yml +++ /dev/null @@ -1,231 +0,0 @@ -name: 'Cora AI Code Review' -description: 'Run cora AI code review on a PR diff — posts a PR comment. Uses GitHub Secrets directly (no Infisical required). SARIF upload is optional.' - -inputs: - base-branch: - description: 'Base branch to compare against (default: origin/develop)' - required: false - default: 'origin/develop' - severity: - description: 'Minimum severity to report (info, minor, major, critical)' - required: false - default: 'major' - cora-version: - description: 'cora-cli version tag (default: latest release)' - required: false - default: 'latest' - upload-sarif: - description: 'Upload SARIF to GitHub Code Scanning (default: false)' - required: false - default: 'false' - cora-api-key: - description: 'LLM API key (from secrets.CORA_API_KEY)' - required: true - cora-base-url: - description: 'LLM API base URL (from secrets.CORA_BASE_URL)' - required: true - cora-model: - description: 'LLM model ID (from secrets.CORA_MODEL)' - required: true - github-token: - description: 'GitHub token for PR comments and SARIF upload' - required: true - -runs: - using: 'composite' - steps: - - name: Resolve cora-cli version - id: resolve - shell: bash - run: | - if [ "${{ inputs.cora-version }}" = "latest" ]; then - # Try up to 3 times with delay - for i in 1 2 3; do - VERSION=$(curl -sfL --connect-timeout 10 \ - https://api.github.com/repos/codecoradev/cora-cli/releases/latest \ - | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('tag_name', ''))" 2>/dev/null) - if [ -n "$VERSION" ]; then - break - fi - echo "Attempt $i failed, retrying..." - sleep 5 - done - # Fallback to a known good version if all retries fail - if [ -z "$VERSION" ]; then - echo "::warning::Failed to resolve latest cora-cli version from GitHub API, using fallback" - VERSION="v0.1.6" - fi - else - VERSION="${{ inputs.cora-version }}" - fi - echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT" - echo "Resolved cora-cli version: $VERSION" - - - name: Install cora-cli - shell: bash - run: | - ARCH=$(uname -m) - if [ "$ARCH" = "x86_64" ]; then - ASSET="cora-x86_64-unknown-linux-gnu" - elif [ "$ARCH" = "aarch64" ]; then - ASSET="cora-aarch64-unknown-linux-gnu" - else - echo "Unsupported architecture: $ARCH" - exit 1 - fi - curl -sL "https://github.com/codecoradev/cora-cli/releases/download/${{ steps.resolve.outputs.VERSION }}/${ASSET}-${{ steps.resolve.outputs.VERSION }}.tar.gz" | tar xz -C /usr/local/bin cora - chmod +x /usr/local/bin/cora - cora --version - - - name: Run cora review - id: review - shell: bash - env: - CORA_API_KEY: ${{ inputs.cora-api-key }} - CORA_BASE_URL: ${{ inputs.cora-base-url }} - CORA_MODEL: ${{ inputs.cora-model }} - run: | - cora review \ - --base ${{ inputs.base-branch }} \ - --format sarif \ - --severity ${{ inputs.severity }} \ - --quiet \ - > cora-results.sarif 2>/dev/null - echo "Cora review complete ($(wc -c < cora-results.sarif) bytes)" - - - name: Check SARIF for findings - id: sarif-check - if: inputs.upload-sarif == 'true' - shell: bash - run: | - RESULTS=$(python3 -c " - import json, sys - try: - data = json.load(open('cora-results.sarif')) - results = data.get('runs', [{}])[0].get('results', []) - print(len(results)) - except: - print(0) - " 2>/dev/null || echo "0") - echo "results=$RESULTS" >> "$GITHUB_OUTPUT" - if [ "$RESULTS" -gt 0 ]; then - echo "SARIF contains $RESULTS finding(s) — will upload." - else - echo "SARIF has no findings — skipping upload." - fi - - - name: Upload SARIF to GitHub Code Scanning - if: inputs.upload-sarif == 'true' && steps.sarif-check.outputs.results > 0 - continue-on-error: true - uses: github/codeql-action/upload-sarif@v4 - with: - sarif_file: cora-results.sarif - category: cora-review - - - name: Post PR comment - if: always() && github.event_name == 'pull_request' - uses: actions/github-script@v7 - env: - GH_TOKEN: ${{ inputs.github-token }} - with: - script: | - const fs = require('fs'); - - let sarifContent; - try { - sarifContent = JSON.parse(fs.readFileSync('cora-results.sarif', 'utf8')); - } catch (e) { - sarifContent = null; - } - - let body; - if (!sarifContent || !sarifContent.runs || sarifContent.runs.length === 0) { - body = `## 🔍 Cora AI Code Review\n\n✅ **No issues found.** Code looks good!`; - } else { - const results = sarifContent.runs[0].results || []; - if (results.length === 0) { - body = `## 🔍 Cora AI Code Review\n\n✅ **No issues found.** Code looks good!`; - } else { - const grouped = {}; - for (const r of results) { - const sev = (r.level || 'note').charAt(0).toUpperCase() + (r.level || 'note').slice(1); - if (!grouped[sev]) grouped[sev] = []; - grouped[sev].push(r); - } - - const severityOrder = ['Error', 'Warning', 'Note']; - let table = ''; - - for (const sev of severityOrder) { - if (!grouped[sev]) continue; - const icon = sev === 'Error' ? '🔴' : sev === 'Warning' ? '🟡' : '🔵'; - table += `\n### ${icon} ${sev} (${grouped[sev].length})\n\n`; - for (const r of grouped[sev]) { - const loc = r.locations?.[0]?.physicalLocation; - const file = loc?.artifactLocation?.uri || 'unknown'; - const line = loc?.region?.startLine || '?'; - const msg = r.message?.text || r.message?.markdown || 'No message'; - table += `- \`${file}:${line}\` — ${msg}\n`; - } - } - - const hasErrors = (grouped['Error'] || []).length > 0; - const hasWarnings = (grouped['Warning'] || []).length > 0; - const status = hasErrors ? '❌ **Blocked** — critical issues found.' : - hasWarnings ? '⚠️ **Review recommended** — warnings found.' : - '✅ **Passed** — only informational notes.'; - - body = `## 🔍 Cora AI Code Review\n\n${status}\n${table}\n---\n_Review powered by [cora-cli](https://github.com/codecoradev/cora-cli) · BYOK · MIT_`; - } - } - - // Find existing cora comment and update it, or create new - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - }); - - const existing = comments.find(c => - c.user.login === 'github-actions[bot]' && - c.body.startsWith('## 🔍 Cora AI Code Review') - ); - - if (existing) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body, - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body, - }); - } - - - name: Check for blocking issues - if: always() - shell: bash - run: | - if [ -f cora-results.sarif ]; then - ERRORS=$(python3 -c " - import json, sys - try: - data = json.load(open('cora-results.sarif')) - results = data.get('runs', [{}])[0].get('results', []) - errors = [r for r in results if r.get('level') == 'error'] - print(len(errors)) - except: - print(0) - " 2>/dev/null || echo "0") - - if [ "$ERRORS" -gt 0 ]; then - echo "::error::Cora found $ERRORS blocking issue(s)." - exit 1 - fi - fi - echo "No blocking issues." diff --git a/docs/examples.md b/docs/examples.md index b7c4594..09868da 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -50,9 +50,108 @@ Stream results as they come in from the LLM. $ cora review --staged --stream ``` -## 06 — GitHub Actions CI +## 06 — GitHub Actions CI (Recommended) -Add cora to your CI pipeline. +The easiest way to add cora to your PR workflow. This reusable action installs cora, runs the review, posts a PR comment with findings, and optionally uploads SARIF to GitHub Code Scanning. + +### Setup + +1. Add these secrets to your repository (**Settings → Secrets and variables → Actions**): + +| Secret | Description | Example | +|--------|-------------|---------| +| `CORA_API_KEY` | Your LLM API key | `sk-...` | +| `CORA_BASE_URL` | LLM API base URL (optional) | `https://api.openai.com/v1` | +| `CORA_MODEL` | LLM model ID (optional) | `gpt-4o-mini` | + +2. Create the workflow file: + +```yaml +# .github/workflows/cora-review.yml +name: Cora AI Code Review + +on: + pull_request_target: + branches: [main, develop] + types: [opened, synchronize, ready_for_review, reopened] + +concurrency: + group: cora-review-${{ github.event.pull_request.number }} + cancel-in-progress: true + +permissions: + contents: read + pull-requests: write + security-events: write + +jobs: + cora-review: + name: Cora Review + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout PR head + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + persist-credentials: false + + - name: Run Cora AI Code Review + uses: codecoradev/cora-cli/.github/actions/cora-review@v0.4.6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + cora-api-key: ${{ secrets.CORA_API_KEY }} + cora-base-url: ${{ secrets.CORA_BASE_URL }} + cora-model: ${{ secrets.CORA_MODEL }} +``` + +### Options + +| Input | Default | Description | +|-------|---------|-------------| +| `base-branch` | `origin/develop` | Base branch to compare against | +| `severity` | `major` | Minimum severity to report (`info`, `minor`, `major`, `critical`) | +| `cora-version` | `latest` | Pin a specific version (e.g., `v0.4.6`) | +| `upload-sarif` | `true` | Upload findings to GitHub Code Scanning | + +### Custom Configuration + +Add a `.cora.yaml` to your project root to customize review behavior: + +```yaml +# .cora.yaml +focus: + - security + - bugs + - performance + +rules: + - No unwrap() in production code + - All public functions must have error handling + +ignore: + files: + - "vendor/**" + - "**/*.generated.*" + rules: + - "style" +``` + +### How It Works + +The action automatically: + +1. **Resolves** the latest cora-cli version from GitHub releases +2. **Downloads** the binary with retry + checksum verification +3. **Runs** `cora review` on the PR diff +4. **Posts** a formatted comment on the PR with findings +5. **Uploads** SARIF results to GitHub Code Scanning (optional) +6. **Fails** the job if blocking issues (severity ≥ `major`) are found + +### Minimal Setup (No Action) + +If you prefer to run cora directly without the reusable action: ```yaml # .github/workflows/cora-review.yml @@ -69,15 +168,12 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Run AI code review + - name: Install and run cora env: CORA_API_KEY: ${{ secrets.CORA_API_KEY }} - CORA_BASE_URL: ${{ secrets.CORA_BASE_URL }} - CORA_MODEL: ${{ secrets.CORA_MODEL }} run: | - # Install cora — pin version with CORA_VERSION=v0.4.5 for reproducibility curl -fsSL https://raw.githubusercontent.com/codecoradev/cora-cli/main/install.sh | sh - cora review --base main --format sarif + cora review --base origin/main --format sarif ``` ## 07 — Pre-commit Hook