mirror of
https://github.com/alibaba/open-code-review.git
synced 2026-07-09 17:28:58 +00:00
Add CI/CD integration section and examples to documentation (#11)
* docs: add CI/CD integration section and examples - Add CI/CD Integration section to README.md and README.zh-CN.md - Add GitHub Actions workflow example (examples/github_actions/) - Add GitLab CI pipeline example (examples/gitlab_ci/) - Add examples README with overview of integration options * feat(examples): enhance GitHub Actions demo with comment trigger and improved error handling - Add issue_comment event trigger with /open-code-review and @open-code-review keywords - Add PR context resolution for comment-triggered events via GitHub API - Improve ref handling to support both PR events and comment events - Add individual comment fallback with retry when batch review fails - Add posting statistics (success/failed counts) to summary comment - Update README with comment trigger flow and customization guide * docs(examples): add --background flag usage guide for GitHub Actions and GitLab CI Explain how to pass PR/MR title as background context to help OCR provide more relevant and context-aware review comments. * feat(examples): simplify PR trigger and add skip-existing-review guide for GitLab CI - Reduce GitHub Actions PR trigger to 'opened' only (avoid redundant reviews on synchronize/reopened events) - Add GitLab CI documentation for checking existing OCR comments before running review to save LLM tokens
This commit is contained in:
parent
ed2302c164
commit
128787b627
7 changed files with 1083 additions and 0 deletions
223
examples/github_actions/README.md
Normal file
223
examples/github_actions/README.md
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
# OpenCodeReview - GitHub Actions Demo
|
||||
|
||||
This demo shows how to integrate OpenCodeReview into your GitHub Actions workflow to automatically review Pull Requests and post review comments.
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
PR Created/Updated → GitHub Actions Triggered → OCR Reviews Diff → Comments Posted on PR
|
||||
OR
|
||||
Comment with trigger keyword ↗
|
||||
```
|
||||
|
||||
1. When a PR is opened, synchronized, or reopened, the workflow triggers
|
||||
2. Alternatively, when a comment containing `/open-code-review` or `@open-code-review` is posted on a PR, the workflow triggers
|
||||
3. It installs OCR via `npm install -g @alibaba-group/open-code-review`
|
||||
4. Runs `ocr review --from origin/<base> --to origin/<head> --format json` to analyze the diff
|
||||
5. Parses the JSON output and posts inline review comments on the PR using GitHub's Pull Request Review API
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Copy the workflow file
|
||||
|
||||
Copy `ocr-review.yml` to your repository's `.github/workflows/` directory:
|
||||
|
||||
```bash
|
||||
mkdir -p .github/workflows
|
||||
cp ocr-review.yml .github/workflows/ocr-review.yml
|
||||
```
|
||||
|
||||
### 2. Configure secrets
|
||||
|
||||
Go to your repository's **Settings → Secrets and variables → Actions** and add:
|
||||
|
||||
| Secret | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `OCR_LLM_URL` | Yes | LLM API endpoint URL (e.g., `https://api.openai.com/v1/chat/completions`) |
|
||||
| `OCR_LLM_AUTH_TOKEN` | Yes | API authentication token |
|
||||
| `OCR_LLM_MODEL` | No | Model name (defaults to `gpt-4o`) |
|
||||
| `OCR_LLM_USE_ANTHROPIC` | No | Set to `true` if using Anthropic Claude models |
|
||||
|
||||
> **Note:** `GITHUB_TOKEN` is automatically provided by GitHub Actions with the required `pull-requests: write` permission.
|
||||
>
|
||||
> The workflow also configures `llm.extra_body` to disable thinking mode for compatibility with various LLM providers.
|
||||
|
||||
## Customization
|
||||
|
||||
### Change the trigger events
|
||||
|
||||
Modify the `on.pull_request.types` array in the workflow file:
|
||||
|
||||
```yaml
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
```
|
||||
|
||||
### Customize comment trigger keywords
|
||||
|
||||
By default, the workflow triggers when a PR comment starts with `/open-code-review` or `@open-code-review`. You can customize these keywords by modifying the `if` condition in the workflow:
|
||||
|
||||
```yaml
|
||||
if: |
|
||||
github.event_name == 'pull_request' ||
|
||||
(github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/review')) ||
|
||||
(github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, '@mybot'))
|
||||
```
|
||||
|
||||
Or use a more flexible pattern with `contains` to trigger on any comment containing the keyword:
|
||||
|
||||
```yaml
|
||||
if: |
|
||||
github.event_name == 'pull_request' ||
|
||||
(github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '/review'))
|
||||
```
|
||||
|
||||
> **Note:** The condition `github.event.issue.pull_request` ensures the comment is on a PR, not a regular issue.
|
||||
|
||||
### Use a specific OCR version
|
||||
|
||||
```yaml
|
||||
- name: Install OpenCodeReview
|
||||
run: npm install -g @alibaba-group/open-code-review@1.0.0
|
||||
```
|
||||
|
||||
### Add custom review rules
|
||||
|
||||
Use the `--rule` flag to pass a custom rules JSON file:
|
||||
|
||||
```yaml
|
||||
- name: Run OCR review
|
||||
run: ocr review --rule ./my-rules.json --from origin/${{ github.base_ref }} --to origin/${{ github.head_ref }}
|
||||
```
|
||||
|
||||
### Limit concurrency
|
||||
|
||||
Adjust the `--concurrency` flag for large PRs to control the number of concurrent LLM requests:
|
||||
|
||||
```yaml
|
||||
- name: Run OCR review
|
||||
run: ocr review --concurrency 5 --from origin/${{ github.base_ref }} --to origin/${{ github.head_ref }}
|
||||
```
|
||||
|
||||
### Provide background context
|
||||
|
||||
Use the `--background` flag to pass additional context that helps OCR better understand the purpose of the changes:
|
||||
|
||||
```yaml
|
||||
- name: Run OCR review
|
||||
run: ocr review --background "${{ github.event.pull_request.title }}" --from origin/${{ github.base_ref }} --to origin/${{ github.head_ref }}
|
||||
```
|
||||
|
||||
This is particularly useful when your PR titles follow semantic conventions (e.g., `feat(auth): add OAuth2 support`) that clearly summarize what the PR implements. The background information helps OCR provide more relevant and context-aware review comments.
|
||||
|
||||
### Customize the review comment author with GitHub App
|
||||
|
||||
By default, review comments are posted using the built-in `GITHUB_TOKEN`, which appears as `github-actions[bot]`. You can customize this by creating a GitHub App and using its credentials instead.
|
||||
|
||||
For more details about GitHub Apps, see the [GitHub Apps documentation](https://docs.github.com/en/apps).
|
||||
|
||||
#### Step 1: Create a GitHub App
|
||||
|
||||
1. Go to your organization or personal account **Settings → Developer settings → GitHub Apps → New GitHub App**
|
||||
2. Fill in the following:
|
||||
- **GitHub App name**: e.g., `OpenCodeReview Bot`
|
||||
- **Homepage URL**: Your repository or documentation URL
|
||||
- **Webhook**: Uncheck "Active" (not needed for this use case)
|
||||
3. Under **Repository permissions**, set:
|
||||
- **Pull requests**: Read and write
|
||||
- **Contents**: Read-only (for fetching diffs)
|
||||
- **Metadata**: Read-only (required)
|
||||
4. Click **Create GitHub App**
|
||||
|
||||
#### Step 2: Generate a Private Key
|
||||
|
||||
1. After creating the app, scroll down to **Private keys**
|
||||
2. Click **Generate a private key**
|
||||
3. Download and save the `.pem` file securely
|
||||
|
||||
Note your App ID from the app settings page.
|
||||
|
||||
#### Step 3: Install the App
|
||||
|
||||
1. In the left sidebar, click **Install App**
|
||||
2. Select the repositories where you want to use OCR
|
||||
3. After installation, note the **Installation ID** from the URL (e.g., `https://github.com/settings/installations/12345` → Installation ID is `12345`)
|
||||
|
||||
#### Step 4: Configure Repository Secrets
|
||||
|
||||
Add the following secrets to your repository (**Settings → Secrets and variables → Actions**):
|
||||
|
||||
| Secret | Description |
|
||||
|--------|-------------|
|
||||
| `GITHUB_APP_ID` | Your GitHub App's ID |
|
||||
| `GITHUB_APP_PRIVATE_KEY` | Contents of the `.pem` file (including `-----BEGIN RSA PRIVATE KEY-----` and `-----END RSA PRIVATE KEY-----`) |
|
||||
| `GITHUB_APP_INSTALLATION_ID` | The Installation ID from Step 3 |
|
||||
|
||||
#### Step 5: Update the Workflow
|
||||
|
||||
Add a step to obtain a token from the GitHub App, then use it in the "Post review comments to PR" step:
|
||||
|
||||
```yaml
|
||||
- name: Get GitHub App Token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.GITHUB_APP_ID }}
|
||||
private-key: ${{ secrets.GITHUB_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Post review comments to PR
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
# ... existing script
|
||||
```
|
||||
|
||||
Now review comments will be posted with your custom GitHub App identity (e.g., `OpenCodeReview Bot`), providing a more professional and distinguishable appearance in your PRs.
|
||||
|
||||
## Example Output
|
||||
|
||||
When a PR is reviewed, comments appear directly in the PR's "Files changed" tab:
|
||||
|
||||
- ✅ If no issues found: A comment saying "No comments generated. Looks good to me."
|
||||
- 🔍 If issues found: Inline review comments with suggestions using GitHub's native suggestion syntax
|
||||
|
||||
### Inline Comment Example
|
||||
|
||||
The workflow uses GitHub's `suggestion` code block syntax, so reviewers can apply fixes with one click:
|
||||
|
||||
````markdown
|
||||
**Suggestion:**
|
||||
```suggestion
|
||||
// Fixed code here
|
||||
```
|
||||
````
|
||||
|
||||
## Supported LLM Providers
|
||||
|
||||
OCR supports both OpenAI and Anthropic API formats:
|
||||
|
||||
- **OpenAI-compatible APIs** (default):
|
||||
- OpenAI (GPT-4o, GPT-4, etc.)
|
||||
- Azure OpenAI
|
||||
- Self-hosted models (vLLM, Ollama, etc.)
|
||||
- **Anthropic APIs** (set `OCR_LLM_USE_ANTHROPIC: true`):
|
||||
- Anthropic Claude models
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **"Failed to parse OCR output"**: Check that `OCR_LLM_URL` and `OCR_LLM_AUTH_TOKEN` secrets are correctly set
|
||||
2. **"Cannot find merge-base"**: Ensure `fetch-depth: 0` is set in the checkout step
|
||||
3. **Review comments not appearing on correct lines**: This can happen when the diff has changed since the review started; the workflow handles this gracefully with a fallback to issue comments
|
||||
|
||||
### Debugging
|
||||
|
||||
Enable debug logging by adding to the OCR review step:
|
||||
|
||||
```yaml
|
||||
env:
|
||||
OCR_DEBUG: "1"
|
||||
```
|
||||
321
examples/github_actions/ocr-review.yml
Normal file
321
examples/github_actions/ocr-review.yml
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
# OpenCodeReview - GitHub Actions PR Auto-Review Demo
|
||||
#
|
||||
# This workflow automatically reviews pull requests using OpenCodeReview
|
||||
# and posts review comments directly on the PR.
|
||||
#
|
||||
# Triggers:
|
||||
# - PR opened, synchronized, or reopened
|
||||
# - Comment on PR containing '/open-code-review' or '@open-code-review'
|
||||
#
|
||||
# Required secrets:
|
||||
# OCR_LLM_URL - LLM API endpoint (e.g., https://api.openai.com/v1/chat/completions)
|
||||
# OCR_LLM_AUTH_TOKEN - Authentication token for the LLM API
|
||||
#
|
||||
# Optional secrets:
|
||||
# OCR_LLM_MODEL - Model name (default: gpt-4o)
|
||||
#
|
||||
# Note: GITHUB_TOKEN is automatically provided by GitHub Actions.
|
||||
|
||||
name: OpenCodeReview PR Review
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
code-review:
|
||||
runs-on: ubuntu-latest
|
||||
# Run on PR events, or on comments starting with trigger keywords
|
||||
if: |
|
||||
github.event_name == 'pull_request' ||
|
||||
(github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/open-code-review')) ||
|
||||
(github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, '@open-code-review'))
|
||||
steps:
|
||||
- name: Get PR context
|
||||
id: pr-context
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
// For issue_comment events, get PR info
|
||||
const prNumber = context.issue.number;
|
||||
const { data: pullRequest } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber
|
||||
});
|
||||
core.setOutput('base_ref', pullRequest.base.ref);
|
||||
core.setOutput('head_ref', pullRequest.head.ref);
|
||||
core.setOutput('head_sha', pullRequest.head.sha);
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Full history needed for merge-base diff
|
||||
ref: ${{ github.event_name != 'pull_request' && steps.pr-context.outputs.head_sha || '' }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install OpenCodeReview
|
||||
run: npm install -g @alibaba-group/open-code-review
|
||||
|
||||
- name: Configure OCR
|
||||
run: |
|
||||
ocr config set llm.url ${{ secrets.OCR_LLM_URL }}
|
||||
ocr config set llm.auth_token ${{ secrets.OCR_LLM_AUTH_TOKEN }}
|
||||
ocr config set llm.model ${{ secrets.OCR_LLM_MODEL }}
|
||||
ocr config set llm.use_anthropic ${{ secrets.OCR_LLM_USE_ANTHROPIC }}
|
||||
ocr config set llm.extra_body '{"thinking": {"type": "disabled"}}'
|
||||
|
||||
- name: Run OpenCodeReview
|
||||
id: review
|
||||
run: |
|
||||
# Get base and head refs from PR context (different for comment triggers)
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
BASE_REF="${{ github.event.pull_request.base.ref }}"
|
||||
HEAD_REF="${{ github.event.pull_request.head.ref }}"
|
||||
else
|
||||
BASE_REF="${{ steps.pr-context.outputs.base_ref }}"
|
||||
HEAD_REF="${{ steps.pr-context.outputs.head_ref }}"
|
||||
fi
|
||||
|
||||
echo "Reviewing PR: ${HEAD_REF} against ${BASE_REF}"
|
||||
|
||||
# Run OCR in range mode with JSON output
|
||||
ocr review \
|
||||
--from "origin/${BASE_REF}" \
|
||||
--to "origin/${HEAD_REF}" \
|
||||
--format json \
|
||||
--audience agent \
|
||||
> /tmp/ocr-result.json 2>/tmp/ocr-stderr.log || true
|
||||
|
||||
echo "OCR review completed. Output:"
|
||||
cat /tmp/ocr-result.json
|
||||
echo "OCR review completed. Error log:"
|
||||
cat /tmp/ocr-stderr.log
|
||||
|
||||
- name: Post review comments to PR
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const path = '/tmp/ocr-result.json';
|
||||
|
||||
// Read OCR output (skip first line which is not valid JSON)
|
||||
let result;
|
||||
try {
|
||||
const raw = fs.readFileSync(path, 'utf8');
|
||||
const jsonContent = raw.substring(raw.indexOf('\n') + 1);
|
||||
result = JSON.parse(jsonContent);
|
||||
} catch (e) {
|
||||
console.log('Failed to parse OCR output:', e.message);
|
||||
// Post a simple comment if parsing fails
|
||||
const stderr = fs.readFileSync('/tmp/ocr-stderr.log', 'utf8').trim();
|
||||
if (stderr) {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: `⚠️ **OpenCodeReview** encountered an error:\n\`\`\`\n${stderr}\n\`\`\``
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const comments = result.comments || [];
|
||||
const warnings = result.warnings || [];
|
||||
|
||||
// If no comments, post a summary
|
||||
if (comments.length === 0) {
|
||||
const message = result.message || 'No comments generated. Looks good to me.';
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: `✅ **OpenCodeReview**: ${message}`
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare PR review with inline comments
|
||||
const prNumber = context.issue.number;
|
||||
let commitSha;
|
||||
|
||||
// Get commit SHA from event context
|
||||
if (context.eventName === 'pull_request') {
|
||||
commitSha = context.payload.pull_request.head.sha;
|
||||
} else {
|
||||
// For comment events, we need to fetch the PR
|
||||
const { data: pullRequest } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber
|
||||
});
|
||||
commitSha = pullRequest.head.sha;
|
||||
}
|
||||
|
||||
// Build review comments array for the PR review API
|
||||
// Only inline comments with line info can be posted via createReview
|
||||
const reviewComments = [];
|
||||
const commentsWithoutLine = [];
|
||||
|
||||
for (const comment of comments) {
|
||||
const body = formatComment(comment);
|
||||
|
||||
// Check if comment has valid line information for inline comment (line >= 1)
|
||||
const hasValidLine = (comment.start_line >= 1) || (comment.end_line >= 1);
|
||||
if (!hasValidLine) {
|
||||
commentsWithoutLine.push({ comment, body });
|
||||
continue;
|
||||
}
|
||||
|
||||
const reviewComment = {
|
||||
path: comment.path,
|
||||
body: body
|
||||
};
|
||||
|
||||
// Use line range if available
|
||||
if (comment.start_line >= 1 && comment.end_line >= 1 && comment.start_line !== comment.end_line) {
|
||||
reviewComment.start_line = comment.start_line;
|
||||
reviewComment.line = comment.end_line;
|
||||
reviewComment.start_side = 'RIGHT';
|
||||
reviewComment.side = 'RIGHT';
|
||||
} else if (comment.end_line >= 1) {
|
||||
reviewComment.line = comment.end_line;
|
||||
reviewComment.side = 'RIGHT';
|
||||
} else if (comment.start_line >= 1) {
|
||||
reviewComment.line = comment.start_line;
|
||||
reviewComment.side = 'RIGHT';
|
||||
}
|
||||
|
||||
reviewComments.push(reviewComment);
|
||||
}
|
||||
|
||||
// Submit as a single PR review with all comments
|
||||
const totalCount = comments.length;
|
||||
const inlineCount = reviewComments.length;
|
||||
const summaryCount = commentsWithoutLine.length;
|
||||
let summaryBody = `🔍 **OpenCodeReview** found **${totalCount}** issue(s) in this PR.`;
|
||||
if (totalCount > 0) {
|
||||
summaryBody += `\n- ✅ ${inlineCount} posted as inline comment(s)`;
|
||||
summaryBody += `\n- 📝 ${summaryCount} posted as summary (missing line info)`;
|
||||
}
|
||||
if (warnings.length > 0) {
|
||||
summaryBody += `\n\n⚠️ ${warnings.length} warning(s) occurred during review.`;
|
||||
}
|
||||
|
||||
// Add comments without line info to summary body
|
||||
for (const { comment, body } of commentsWithoutLine) {
|
||||
summaryBody += '\n\n---\n\n';
|
||||
summaryBody += formatCommentMarkdown(comment);
|
||||
}
|
||||
|
||||
// Statistics tracking
|
||||
let successCount = 0;
|
||||
let failedCount = 0;
|
||||
const failedComments = [];
|
||||
|
||||
try {
|
||||
await github.rest.pulls.createReview({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
commit_id: commitSha,
|
||||
body: summaryBody,
|
||||
event: 'COMMENT',
|
||||
comments: reviewComments
|
||||
});
|
||||
successCount = reviewComments.length;
|
||||
console.log(`Successfully posted review with ${successCount} inline comments (${commentsWithoutLine.length} in summary)`);
|
||||
} catch (e) {
|
||||
console.log('Failed to post review with inline comments:', e.message);
|
||||
console.log('Falling back to posting comments individually...');
|
||||
|
||||
// Fallback: post comments one by one
|
||||
for (const reviewComment of reviewComments) {
|
||||
try {
|
||||
await github.rest.pulls.createReview({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
commit_id: commitSha,
|
||||
body: '',
|
||||
event: 'COMMENT',
|
||||
comments: [reviewComment]
|
||||
});
|
||||
successCount++;
|
||||
console.log(`Successfully posted comment for ${reviewComment.path}`);
|
||||
} catch (innerE) {
|
||||
failedCount++;
|
||||
failedComments.push({ comment: reviewComment, error: innerE.message });
|
||||
console.log(`Failed to post comment for ${reviewComment.path}: ${innerE.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Post summary comment with statistics
|
||||
let finalBody = summaryBody;
|
||||
finalBody += `\n\n---\n\n📊 **Posting Statistics:**`;
|
||||
finalBody += `\n- ✅ Successfully posted: ${successCount} comment(s)`;
|
||||
if (failedCount > 0) {
|
||||
finalBody += `\n- ❌ Failed to post: ${failedCount} comment(s)`;
|
||||
}
|
||||
|
||||
// Add failed comments details
|
||||
if (failedComments.length > 0) {
|
||||
finalBody += '\n\n<details><summary>❌ Failed Comments Details</summary>\n\n';
|
||||
for (const { comment, error } of failedComments) {
|
||||
finalBody += `- \`${comment.path}\`: ${error}\n`;
|
||||
}
|
||||
finalBody += '\n</details>';
|
||||
}
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body: finalBody
|
||||
});
|
||||
}
|
||||
|
||||
function formatComment(comment) {
|
||||
let body = comment.content || '';
|
||||
|
||||
// Add code suggestion if available
|
||||
if (comment.suggestion_code && comment.existing_code) {
|
||||
body += '\n\n**Suggestion:**\n';
|
||||
body += '```suggestion\n';
|
||||
body += comment.suggestion_code;
|
||||
if (!comment.suggestion_code.endsWith('\n')) body += '\n';
|
||||
body += '```';
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
function formatCommentMarkdown(comment) {
|
||||
let md = `### 📄 \`${comment.path}\``;
|
||||
if (comment.start_line && comment.end_line) {
|
||||
md += ` (L${comment.start_line}-L${comment.end_line})`;
|
||||
}
|
||||
md += '\n\n';
|
||||
md += comment.content || '';
|
||||
|
||||
if (comment.suggestion_code && comment.existing_code) {
|
||||
md += '\n\n<details><summary>💡 Suggested Change</summary>\n\n';
|
||||
md += '**Before:**\n```\n' + comment.existing_code + '\n```\n\n';
|
||||
md += '**After:**\n```\n' + comment.suggestion_code + '\n```\n\n';
|
||||
md += '</details>';
|
||||
}
|
||||
|
||||
return md;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue